mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-15 15:17:26 +00:00
Compare commits
98 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7cfce9ff7a | ||
![]() |
7f088d6241 | ||
![]() |
d11038f3de | ||
![]() |
6df42a4be7 | ||
![]() |
7fd111b91f | ||
![]() |
dd7dc2ec5a | ||
![]() |
86c586d882 | ||
![]() |
66ac6f72fc | ||
![]() |
f21f448099 | ||
![]() |
548d70f30c | ||
![]() |
39e714c6d8 | ||
![]() |
9968af0785 | ||
![]() |
be7586137c | ||
![]() |
7999b66c3c | ||
![]() |
c82a46c1ee | ||
![]() |
666ab1941f | ||
![]() |
71e37345b4 | ||
![]() |
e7c82f20e3 | ||
![]() |
afa771a980 | ||
![]() |
0d1de98cca | ||
![]() |
02bf7dca01 | ||
![]() |
8cc76b1d86 | ||
![]() |
77a275cbcd | ||
![]() |
3956cbe2d2 | ||
![]() |
945de8d9a0 | ||
![]() |
6dabd3bb2d | ||
![]() |
4c80808997 | ||
![]() |
5a39f7cdde | ||
![]() |
5d400fbe90 | ||
![]() |
e36596470c | ||
![]() |
668e549208 | ||
![]() |
256ff31d11 | ||
![]() |
2414d5d7f5 | ||
![]() |
b7fc15d399 | ||
![]() |
c09b4dabc4 | ||
![]() |
a4aa4a91a3 | ||
![]() |
8f0ea5925a | ||
![]() |
936ad1aa20 | ||
![]() |
d021bca6ef | ||
![]() |
55ed6109c1 | ||
![]() |
f6d765bf81 | ||
![]() |
88e8f2bf83 | ||
![]() |
c849759682 | ||
![]() |
605eae21bc | ||
![]() |
93eb277a88 | ||
![]() |
8edf556c9e | ||
![]() |
7fcb63230f | ||
![]() |
12093a3dad | ||
![]() |
ebb0ec6c42 | ||
![]() |
188546515c | ||
![]() |
c8990b0f68 | ||
![]() |
7dced4b9d9 | ||
![]() |
3145e67feb | ||
![]() |
e9348d9b6a | ||
![]() |
1a1b346c05 | ||
![]() |
920d059837 | ||
![]() |
bef5c3bd1b | ||
![]() |
97037f7d03 | ||
![]() |
a7392ed3d7 | ||
![]() |
3eb1a7e384 | ||
![]() |
1ecdc78c2f | ||
![]() |
d279dba37e | ||
![]() |
a4f97fa151 | ||
![]() |
ff7ac582f0 | ||
![]() |
d2c2456fbe | ||
![]() |
e9f562a8b7 | ||
![]() |
084e0a73dc | ||
![]() |
10f991b8d0 | ||
![]() |
79620c97d1 | ||
![]() |
ffec9a4ddd | ||
![]() |
9b18960bbd | ||
![]() |
a009fdbdc3 | ||
![]() |
c1fc3f373c | ||
![]() |
f4cf5dc0cd | ||
![]() |
355341f0ab | ||
![]() |
7f65f7d3ca | ||
![]() |
9fa096c6f4 | ||
![]() |
70415a396a | ||
![]() |
c921964938 | ||
![]() |
3bf47a6838 | ||
![]() |
d3d28f0623 | ||
![]() |
f880b57544 | ||
![]() |
32b7a26fa6 | ||
![]() |
32fc34f922 | ||
![]() |
b82a393692 | ||
![]() |
3c7e792167 | ||
![]() |
0ad66875ab | ||
![]() |
1191ac2671 | ||
![]() |
928b3425e3 | ||
![]() |
0726a00e3b | ||
![]() |
5a88984d34 | ||
![]() |
18de60f68c | ||
![]() |
1893359142 | ||
![]() |
f5e5ab2436 | ||
![]() |
ff5ea1a70d | ||
![]() |
54ee63a409 | ||
![]() |
f095606b50 | ||
![]() |
e8f31c78d7 |
16
README.MD
16
README.MD
@@ -12,19 +12,19 @@ Some highlight features:
|
|||||||
- **MagiskSU**: Provide root access for applications
|
- **MagiskSU**: Provide root access for applications
|
||||||
- **Magisk Modules**: Modify read-only partitions by installing modules
|
- **Magisk Modules**: Modify read-only partitions by installing modules
|
||||||
- **MagiskBoot**: The most complete tool for unpacking and repacking Android boot images
|
- **MagiskBoot**: The most complete tool for unpacking and repacking Android boot images
|
||||||
|
- **Zygisk**: Run code in every Android applications' processes
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
[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/v23.0)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.1)
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v23.0)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.1)
|
||||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
|
|
||||||
- [Installation Instruction](https://topjohnwu.github.io/Magisk/install.html)
|
- [Installation Instruction](https://topjohnwu.github.io/Magisk/install.html)
|
||||||
- [Frequently Asked Questions](https://topjohnwu.github.io/Magisk/faq.html)
|
|
||||||
- [Magisk Documentation](https://topjohnwu.github.io/Magisk/)
|
- [Magisk Documentation](https://topjohnwu.github.io/Magisk/)
|
||||||
- [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan))
|
- [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan))
|
||||||
|
|
||||||
@@ -41,15 +41,15 @@ For Magisk app crashes, record and upload the logcat when the crash occurs.
|
|||||||
- Magisk builds on any OS Android Studio supports. Install Android Studio and do the initial setups.
|
- Magisk builds on any OS Android Studio supports. Install Android Studio and do the initial setups.
|
||||||
- Clone sources: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
|
- Clone sources: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
|
||||||
- Install Python 3.6+ \
|
- Install Python 3.6+ \
|
||||||
(Windows only: select **'Add Python to PATH'** in installer, and run `pip install colorama` after install)
|
(Windows only: select **'Add Python to PATH'** in installer, and run `pip install colorama` after install)
|
||||||
- Configure to use the JDK bundled in Android Studio:
|
- Configure to use the JDK bundled in Android Studio:
|
||||||
- macOS: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/Contents/Home"`
|
- macOS: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/Contents/Home"`
|
||||||
- Linux: `export PATH="/path/to/androidstudio/jre/bin:$PATH"`
|
- Linux: `export PATH="/path/to/androidstudio/jre/bin:$PATH"`
|
||||||
- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH`
|
- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH`
|
||||||
- Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings)
|
- Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings)
|
||||||
- Run `./build.py ndk` to let the script download and install NDK for you
|
- Run `./build.py ndk` to let the script download and install NDK for you
|
||||||
- To start building, run `build.py` to see your options. \
|
- To start building, run `build.py` to see your options. \
|
||||||
For each action, use `-h` to access help (e.g. `./build.py all -h`)
|
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 (C++/C) sources.
|
||||||
- Optionally, set custom configs with `config.prop`. A sample `config.prop.sample` is provided.
|
- Optionally, set custom configs with `config.prop`. A sample `config.prop.sample` is provided.
|
||||||
- To sign APKs and zips with your own private keys, set signing configs in `config.prop`. For more info, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).
|
- To sign APKs and zips with your own private keys, set signing configs in `config.prop`. For more info, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).
|
||||||
|
@@ -57,6 +57,7 @@ android {
|
|||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
freeCompilerArgs = listOf("-Xjvm-default=enable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ dependencies {
|
|||||||
implementation("${bindingAdapter}:${vBAdapt}")
|
implementation("${bindingAdapter}:${vBAdapt}")
|
||||||
implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
|
implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
|
||||||
|
|
||||||
val vLibsu = "3.2.1"
|
val vLibsu = "4.0.0"
|
||||||
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
||||||
implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
|
implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
|
||||||
implementation("com.github.topjohnwu.libsu:service:${vLibsu}")
|
implementation("com.github.topjohnwu.libsu:service:${vLibsu}")
|
||||||
@@ -109,19 +110,19 @@ dependencies {
|
|||||||
implementation("androidx.room:room-ktx:${vRoom}")
|
implementation("androidx.room:room-ktx:${vRoom}")
|
||||||
kapt("androidx.room:room-compiler:${vRoom}")
|
kapt("androidx.room:room-compiler:${vRoom}")
|
||||||
|
|
||||||
val vNav = "2.4.0-rc01"
|
val vNav = "2.5.0-alpha01"
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
|
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
||||||
|
|
||||||
implementation("androidx.biometric:biometric:1.1.0")
|
implementation("androidx.biometric:biometric:1.1.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.2")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.4.1")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
implementation("androidx.preference:preference:1.1.1")
|
implementation("androidx.preference:preference:1.2.0")
|
||||||
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||||
implementation("androidx.fragment:fragment-ktx:1.4.0")
|
implementation("androidx.fragment:fragment-ktx:1.4.1")
|
||||||
implementation("androidx.transition:transition:1.4.1")
|
implementation("androidx.transition:transition:1.4.1")
|
||||||
implementation("androidx.core:core-ktx:1.7.0")
|
implementation("androidx.core:core-ktx:1.7.0")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.0-beta01")
|
implementation("androidx.core:core-splashscreen:1.0.0-beta01")
|
||||||
implementation("com.google.android.material:material:1.4.0")
|
implementation("com.google.android.material:material:1.5.0")
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,10 @@ package com.topjohnwu.magisk;
|
|||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -12,28 +15,39 @@ import java.util.Map;
|
|||||||
import io.michaelrocks.paranoid.Obfuscate;
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
@Obfuscate
|
@Obfuscate
|
||||||
public class DynAPK {
|
public class StubApk {
|
||||||
private static File dynDir;
|
private static File dynDir;
|
||||||
private static Method addAssetPath;
|
private static Method addAssetPath;
|
||||||
|
|
||||||
private static File getDynDir(Context c) {
|
private static File getDynDir(ApplicationInfo info) {
|
||||||
if (dynDir == null) {
|
if (dynDir == null) {
|
||||||
|
final String dataDir;
|
||||||
if (SDK_INT >= 24) {
|
if (SDK_INT >= 24) {
|
||||||
// Use protected context to allow directBootAware
|
// Use device protected path to allow directBootAware
|
||||||
c = c.createDeviceProtectedStorageContext();
|
dataDir = info.deviceProtectedDataDir;
|
||||||
|
} else {
|
||||||
|
dataDir = info.dataDir;
|
||||||
}
|
}
|
||||||
dynDir = new File(c.getFilesDir().getParent(), "dyn");
|
dynDir = new File(dataDir, "dyn");
|
||||||
dynDir.mkdir();
|
dynDir.mkdirs();
|
||||||
}
|
}
|
||||||
return dynDir;
|
return dynDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File current(Context c) {
|
public static File current(Context c) {
|
||||||
return new File(getDynDir(c), "current.apk");
|
return new File(getDynDir(c.getApplicationInfo()), "current.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File current(ApplicationInfo info) {
|
||||||
|
return new File(getDynDir(info), "current.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File update(Context c) {
|
public static File update(Context c) {
|
||||||
return new File(getDynDir(c), "update.apk");
|
return new File(getDynDir(c.getApplicationInfo()), "update.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File update(ApplicationInfo info) {
|
||||||
|
return new File(getDynDir(info), "update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addAssetPath(AssetManager asset, String path) {
|
public static void addAssetPath(AssetManager asset, String path) {
|
||||||
@@ -44,6 +58,14 @@ public class DynAPK {
|
|||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void restartProcess(Activity activity) {
|
||||||
|
Intent intent = activity.getPackageManager()
|
||||||
|
.getLaunchIntentForPackage(activity.getPackageName());
|
||||||
|
activity.finishAffinity();
|
||||||
|
activity.startActivity(intent);
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Data {
|
public static class Data {
|
||||||
// Indices of the object array
|
// Indices of the object array
|
||||||
private static final int STUB_VERSION = 0;
|
private static final int STUB_VERSION = 0;
|
@@ -1,5 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import static android.content.pm.PackageInstaller.EXTRA_SESSION_ID;
|
||||||
import static android.content.pm.PackageInstaller.EXTRA_STATUS;
|
import static android.content.pm.PackageInstaller.EXTRA_STATUS;
|
||||||
import static android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID;
|
import static android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID;
|
||||||
import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
|
import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
|
||||||
@@ -10,17 +11,17 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.PackageInstaller.Session;
|
|
||||||
import android.content.pm.PackageInstaller.SessionParams;
|
import android.content.pm.PackageInstaller.SessionParams;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -28,29 +29,6 @@ import io.michaelrocks.paranoid.Obfuscate;
|
|||||||
|
|
||||||
@Obfuscate
|
@Obfuscate
|
||||||
public final class APKInstall {
|
public final class APKInstall {
|
||||||
// @WorkerThread
|
|
||||||
public static void installapk(Context context, File apk) {
|
|
||||||
//noinspection InlinedApi
|
|
||||||
var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
|
|
||||||
var action = APKInstall.class.getName();
|
|
||||||
var intent = new Intent(action).setPackage(context.getPackageName());
|
|
||||||
var pending = PendingIntent.getBroadcast(context, 0, intent, flag);
|
|
||||||
|
|
||||||
var installer = context.getPackageManager().getPackageInstaller();
|
|
||||||
var params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
params.setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED);
|
|
||||||
}
|
|
||||||
try (Session session = installer.openSession(installer.createSession(params))) {
|
|
||||||
OutputStream out = session.openWrite(apk.getName(), 0, apk.length());
|
|
||||||
try (var in = new FileInputStream(apk); out) {
|
|
||||||
transfer(in, out);
|
|
||||||
}
|
|
||||||
session.commit(pending.getIntentSender());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(APKInstall.class.getSimpleName(), "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void transfer(InputStream in, OutputStream out) throws IOException {
|
public static void transfer(InputStream in, OutputStream out) throws IOException {
|
||||||
int size = 8192;
|
int size = 8192;
|
||||||
@@ -61,61 +39,136 @@ public final class APKInstall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstallReceiver register(Context context, String packageName, Runnable onSuccess) {
|
public static Session startSession(Context context) {
|
||||||
var receiver = new InstallReceiver(context, packageName, onSuccess);
|
return startSession(context, null, null, null);
|
||||||
var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
}
|
||||||
filter.addDataScheme("package");
|
|
||||||
context.registerReceiver(receiver, filter);
|
public static Session startSession(Context context, String pkg,
|
||||||
context.registerReceiver(receiver, new IntentFilter(APKInstall.class.getName()));
|
Runnable onFailure, Runnable onSuccess) {
|
||||||
|
var receiver = new InstallReceiver(pkg, onSuccess, onFailure);
|
||||||
|
context = context.getApplicationContext();
|
||||||
|
if (pkg != null) {
|
||||||
|
// If pkg is not null, look for package added event
|
||||||
|
var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||||
|
filter.addDataScheme("package");
|
||||||
|
context.registerReceiver(receiver, filter);
|
||||||
|
}
|
||||||
|
context.registerReceiver(receiver, new IntentFilter(receiver.sessionId));
|
||||||
return receiver;
|
return receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InstallReceiver extends BroadcastReceiver {
|
public interface Session {
|
||||||
private final Context context;
|
// @WorkerThread
|
||||||
|
OutputStream openStream(Context context) throws IOException;
|
||||||
|
// @WorkerThread
|
||||||
|
void install(Context context, File apk) throws IOException;
|
||||||
|
// @WorkerThread @Nullable
|
||||||
|
Intent waitIntent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InstallReceiver extends BroadcastReceiver implements Session {
|
||||||
private final String packageName;
|
private final String packageName;
|
||||||
private final Runnable onSuccess;
|
private final Runnable onSuccess;
|
||||||
|
private final Runnable onFailure;
|
||||||
private final CountDownLatch latch = new CountDownLatch(1);
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
private Intent intent = null;
|
private Intent userAction = null;
|
||||||
|
|
||||||
private InstallReceiver(Context context, String packageName, Runnable onSuccess) {
|
final String sessionId = UUID.randomUUID().toString();
|
||||||
this.context = context;
|
|
||||||
|
private InstallReceiver(String packageName, Runnable onSuccess, Runnable onFailure) {
|
||||||
this.packageName = packageName;
|
this.packageName = packageName;
|
||||||
this.onSuccess = onSuccess;
|
this.onSuccess = onSuccess;
|
||||||
|
this.onFailure = onFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context c, Intent i) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (Intent.ACTION_PACKAGE_ADDED.equals(i.getAction())) {
|
if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
|
||||||
Uri data = i.getData();
|
Uri data = intent.getData();
|
||||||
if (data == null || onSuccess == null) return;
|
if (data == null)
|
||||||
|
return;
|
||||||
String pkg = data.getSchemeSpecificPart();
|
String pkg = data.getSchemeSpecificPart();
|
||||||
if (pkg.equals(packageName)) {
|
if (pkg.equals(packageName)) {
|
||||||
onSuccess.run();
|
onSuccess(context);
|
||||||
context.unregisterReceiver(this);
|
|
||||||
}
|
}
|
||||||
return;
|
} else if (sessionId.equals(intent.getAction())) {
|
||||||
|
int status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID);
|
||||||
|
switch (status) {
|
||||||
|
case STATUS_PENDING_USER_ACTION:
|
||||||
|
userAction = intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||||
|
break;
|
||||||
|
case STATUS_SUCCESS:
|
||||||
|
if (packageName == null) {
|
||||||
|
onSuccess(context);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
int id = intent.getIntExtra(EXTRA_SESSION_ID, 0);
|
||||||
|
if (id > 0) {
|
||||||
|
var installer = context.getPackageManager().getPackageInstaller();
|
||||||
|
var info = installer.getSessionInfo(id);
|
||||||
|
if (info != null) {
|
||||||
|
installer.abandonSession(info.getSessionId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (onFailure != null) {
|
||||||
|
onFailure.run();
|
||||||
|
}
|
||||||
|
context.getApplicationContext().unregisterReceiver(this);
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
}
|
}
|
||||||
int status = i.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID);
|
|
||||||
switch (status) {
|
|
||||||
case STATUS_PENDING_USER_ACTION:
|
|
||||||
intent = i.getParcelableExtra(Intent.EXTRA_INTENT);
|
|
||||||
break;
|
|
||||||
case STATUS_SUCCESS:
|
|
||||||
if (onSuccess != null) onSuccess.run();
|
|
||||||
default:
|
|
||||||
context.unregisterReceiver(this);
|
|
||||||
}
|
|
||||||
latch.countDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @WorkerThread @Nullable
|
private void onSuccess(Context context) {
|
||||||
|
if (onSuccess != null)
|
||||||
|
onSuccess.run();
|
||||||
|
context.getApplicationContext().unregisterReceiver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Intent waitIntent() {
|
public Intent waitIntent() {
|
||||||
try {
|
try {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
// noinspection ResultOfMethodCallIgnored
|
||||||
latch.await(5, TimeUnit.SECONDS);
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {}
|
||||||
|
return userAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream openStream(Context context) throws IOException {
|
||||||
|
// noinspection InlinedApi
|
||||||
|
var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
|
||||||
|
var intent = new Intent(sessionId).setPackage(context.getPackageName());
|
||||||
|
var pending = PendingIntent.getBroadcast(context, 0, intent, flag);
|
||||||
|
|
||||||
|
var installer = context.getPackageManager().getPackageInstaller();
|
||||||
|
var params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
params.setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED);
|
||||||
|
}
|
||||||
|
var session = installer.openSession(installer.createSession(params));
|
||||||
|
var out = session.openWrite(sessionId, 0, -1);
|
||||||
|
return new FilterOutputStream(out) {
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
out.write(b, off, len);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
session.commit(pending.getIntentSender());
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void install(Context context, File apk) throws IOException {
|
||||||
|
try (var src = new FileInputStream(apk);
|
||||||
|
var out = openStream(context)) {
|
||||||
|
transfer(src, out);
|
||||||
}
|
}
|
||||||
return intent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
android:directBootAware="true"
|
android:directBootAware="true"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
android:taskAffinity=""
|
||||||
tools:ignore="AppLinkUrlError">
|
tools:ignore="AppLinkUrlError">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
<action android:name="android.intent.action.UID_REMOVED" />
|
<action android:name="android.intent.action.UID_REMOVED" />
|
||||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
|
@@ -15,11 +15,11 @@ import com.topjohnwu.magisk.ktx.startAnimations
|
|||||||
|
|
||||||
abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHolder {
|
abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHolder {
|
||||||
|
|
||||||
val activity get() = requireActivity() as NavigationActivity<*>
|
val activity get() = getActivity() as? NavigationActivity<*>
|
||||||
protected lateinit var binding: Binding
|
protected lateinit var binding: Binding
|
||||||
protected abstract val layoutRes: Int
|
protected abstract val layoutRes: Int
|
||||||
|
|
||||||
private val navigation get() = activity.navigation
|
private val navigation get() = activity?.navigation
|
||||||
open val snackbarAnchorView: View? get() = null
|
open val snackbarAnchorView: View? get() = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -41,12 +41,12 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
|
|||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
activity.supportActionBar?.subtitle = null
|
activity?.supportActionBar?.subtitle = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
||||||
is ContextExecutor -> event(requireContext())
|
is ContextExecutor -> event(requireContext())
|
||||||
is ActivityExecutor -> event(activity)
|
is ActivityExecutor -> activity?.let { event(it) } ?: Unit
|
||||||
is FragmentExecutor -> event(this)
|
is FragmentExecutor -> event(this)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,27 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
import android.content.Context
|
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||||
import android.content.Intent
|
import android.annotation.SuppressLint
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
import android.widget.Toast
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.*
|
import com.topjohnwu.magisk.core.Config
|
||||||
|
import com.topjohnwu.magisk.core.Const
|
||||||
|
import com.topjohnwu.magisk.core.JobService
|
||||||
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.ui.theme.Theme
|
import com.topjohnwu.magisk.ui.theme.Theme
|
||||||
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import com.topjohnwu.magisk.view.Shortcuts
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import java.util.concurrent.CountDownLatch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<Binding>() {
|
abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<Binding>() {
|
||||||
|
|
||||||
@@ -25,9 +29,6 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
|
|||||||
private var doPreload = true
|
private var doPreload = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private val latch = CountDownLatch(1)
|
|
||||||
private val uninstallPkg = registerForActivityResult(UninstallPackage) { latch.countDown() }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setTheme(Theme.selected.themeRes)
|
setTheme(Theme.selected.themeRes)
|
||||||
|
|
||||||
@@ -67,18 +68,28 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
|
|||||||
|
|
||||||
abstract fun showMainUI(savedInstanceState: Bundle?)
|
abstract fun showMainUI(savedInstanceState: Bundle?)
|
||||||
|
|
||||||
private fun showInvalidStateMessage() {
|
@SuppressLint("InlinedApi")
|
||||||
runOnUiThread {
|
private fun showInvalidStateMessage(): Unit = runOnUiThread {
|
||||||
MagiskDialog(this).apply {
|
MagiskDialog(this).apply {
|
||||||
setTitle(R.string.unsupport_nonroot_stub_title)
|
setTitle(R.string.unsupport_nonroot_stub_title)
|
||||||
setMessage(R.string.unsupport_nonroot_stub_msg)
|
setMessage(R.string.unsupport_nonroot_stub_msg)
|
||||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
text = R.string.install
|
text = R.string.install
|
||||||
onClick { HideAPK.restore(this@BaseMainActivity) }
|
onClick {
|
||||||
|
withPermission(REQUEST_INSTALL_PACKAGES) {
|
||||||
|
if (!it) {
|
||||||
|
Utils.toast(R.string.install_unknown_denied, Toast.LENGTH_SHORT)
|
||||||
|
showInvalidStateMessage()
|
||||||
|
} else {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
HideAPK.restore(this@BaseMainActivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setCancelable(false)
|
|
||||||
show()
|
|
||||||
}
|
}
|
||||||
|
setCancelable(false)
|
||||||
|
show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,30 +111,17 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
|
|||||||
runCatching {
|
runCatching {
|
||||||
// Hidden, remove com.topjohnwu.magisk if exist as it could be malware
|
// Hidden, remove com.topjohnwu.magisk if exist as it could be malware
|
||||||
packageManager.getApplicationInfo(APPLICATION_ID, 0)
|
packageManager.getApplicationInfo(APPLICATION_ID, 0)
|
||||||
Shell.su("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
|
Shell.cmd("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Config.suManager.isNotEmpty())
|
if (Config.suManager.isNotEmpty())
|
||||||
Config.suManager = ""
|
Config.suManager = ""
|
||||||
pkg ?: return
|
pkg ?: return
|
||||||
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess) {
|
if (!Shell.cmd("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess) {
|
||||||
uninstallPkg.launch(pkg)
|
// Uninstall through Android API
|
||||||
// Wait for the uninstallation to finish
|
uninstallAndWait(pkg)
|
||||||
latch.await()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object UninstallPackage : ActivityResultContract<String, Boolean>() {
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
override fun createIntent(context: Context, input: String): Intent {
|
|
||||||
val uri = Uri.Builder().scheme("package").opaquePart(input).build()
|
|
||||||
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
|
|
||||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
|
||||||
return intent
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun parseResult(resultCode: Int, intent: Intent?) = resultCode == RESULT_OK
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||||
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import androidx.databinding.Observable
|
import androidx.databinding.Observable
|
||||||
@@ -78,7 +80,7 @@ abstract class BaseViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline fun withExternalRW(crossinline callback: () -> Unit) {
|
inline fun withExternalRW(crossinline callback: () -> Unit) {
|
||||||
withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
withPermission(WRITE_EXTERNAL_STORAGE) {
|
||||||
if (!it) {
|
if (!it) {
|
||||||
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
||||||
} else {
|
} else {
|
||||||
@@ -87,6 +89,17 @@ abstract class BaseViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
inline fun withInstallPermission(crossinline callback: () -> Unit) {
|
||||||
|
withPermission(REQUEST_INSTALL_PACKAGES) {
|
||||||
|
if (!it) {
|
||||||
|
SnackbarEvent(R.string.install_unknown_denied).publish()
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun back() = BackPressEvent().publish()
|
fun back() = BackPressEvent().publish()
|
||||||
|
|
||||||
fun <Event : ViewEvent> Event.publish() {
|
fun <Event : ViewEvent> Event.publish() {
|
||||||
|
@@ -10,6 +10,7 @@ import androidx.core.content.res.use
|
|||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||||
@@ -74,6 +75,13 @@ abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModel
|
|||||||
binding.root.rootView.accessibilityDelegate = delegate
|
binding.root.rootView.accessibilityDelegate = delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showSnackbar(
|
||||||
|
message: CharSequence,
|
||||||
|
length: Int = Snackbar.LENGTH_SHORT,
|
||||||
|
builder: Snackbar.() -> Unit = {}
|
||||||
|
) = Snackbar.make(snackbarView, message, length)
|
||||||
|
.setAnchorView(snackbarAnchorView).apply(builder).show()
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
viewModel.requestRefresh()
|
viewModel.requestRefresh()
|
||||||
|
@@ -6,9 +6,10 @@ import android.app.Application
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.utils.*
|
import com.topjohnwu.magisk.core.utils.*
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import com.topjohnwu.superuser.ipc.RootService
|
import com.topjohnwu.superuser.ipc.RootService
|
||||||
@@ -19,7 +20,7 @@ import kotlin.system.exitProcess
|
|||||||
open class App() : Application() {
|
open class App() : Application() {
|
||||||
|
|
||||||
constructor(o: Any) : this() {
|
constructor(o: Any) : this() {
|
||||||
val data = DynAPK.Data(o)
|
val data = StubApk.Data(o)
|
||||||
// Add the root service name mapping
|
// Add the root service name mapping
|
||||||
data.classToComponent[RootRegistry::class.java.name] = data.rootService.name
|
data.classToComponent[RootRegistry::class.java.name] = data.rootService.name
|
||||||
// Send back the actual root service class
|
// Send back the actual root service class
|
||||||
@@ -58,18 +59,18 @@ open class App() : Application() {
|
|||||||
|
|
||||||
refreshLocale()
|
refreshLocale()
|
||||||
AppApkPath = if (isRunningAsStub) {
|
AppApkPath = if (isRunningAsStub) {
|
||||||
DynAPK.current(base).path
|
StubApk.current(base).path
|
||||||
} else {
|
} else {
|
||||||
base.packageResourcePath
|
base.packageResourcePath
|
||||||
}
|
}
|
||||||
|
|
||||||
base.resources.patch()
|
base.resources.patch()
|
||||||
app.registerActivityLifecycleCallbacks(ForegroundTracker)
|
app.registerActivityLifecycleCallbacks(ActivityTracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
RootRegistry.bindTask = RootService.createBindTask(
|
RootRegistry.bindTask = RootService.bindOrTask(
|
||||||
intent<RootRegistry>(),
|
intent<RootRegistry>(),
|
||||||
UiThreadHandler.executor,
|
UiThreadHandler.executor,
|
||||||
RootRegistry.Connection
|
RootRegistry.Connection
|
||||||
@@ -86,18 +87,18 @@ open class App() : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
object ForegroundTracker : Application.ActivityLifecycleCallbacks {
|
object ActivityTracker : Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
var foreground: Activity? = null
|
var foreground: Activity? = null
|
||||||
|
|
||||||
val hasForeground get() = foreground != null
|
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
if (activity is SuRequestActivity) return
|
||||||
foreground = activity
|
foreground = activity
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityPaused(activity: Activity) {
|
override fun onActivityPaused(activity: Activity) {
|
||||||
|
if (activity is SuRequestActivity) return
|
||||||
foreground = null
|
foreground = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,8 +37,6 @@ object Const {
|
|||||||
|
|
||||||
object ID {
|
object ID {
|
||||||
const val JOB_SERVICE_ID = 7
|
const val JOB_SERVICE_ID = 7
|
||||||
const val UPDATE_NOTIFICATION_CHANNEL = "update"
|
|
||||||
const val PROGRESS_NOTIFICATION_CHANNEL = "progress"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Url {
|
object Url {
|
||||||
|
@@ -11,14 +11,14 @@ import android.content.res.AssetManager
|
|||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import com.topjohnwu.magisk.DynAPK
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.utils.syncLocale
|
import com.topjohnwu.magisk.core.utils.syncLocale
|
||||||
import com.topjohnwu.magisk.di.AppContext
|
import com.topjohnwu.magisk.di.AppContext
|
||||||
|
|
||||||
lateinit var AppApkPath: String
|
lateinit var AppApkPath: String
|
||||||
|
|
||||||
fun AssetManager.addAssetPath(path: String) = DynAPK.addAssetPath(this, path)
|
fun AssetManager.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
||||||
|
|
||||||
fun Context.wrap(): Context = if (this is PatchedContext) this else PatchedContext(this)
|
fun Context.wrap(): Context = if (this is PatchedContext) this else PatchedContext(this)
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.core
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.databinding.ObservableBoolean
|
import androidx.databinding.ObservableBoolean
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||||
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||||
@@ -16,7 +16,7 @@ val isRunningAsStub get() = Info.stub != null
|
|||||||
|
|
||||||
object Info {
|
object Info {
|
||||||
|
|
||||||
var stub: DynAPK.Data? = null
|
var stub: StubApk.Data? = null
|
||||||
|
|
||||||
val EMPTY_REMOTE = UpdateInfo()
|
val EMPTY_REMOTE = UpdateInfo()
|
||||||
var remote = EMPTY_REMOTE
|
var remote = EMPTY_REMOTE
|
||||||
|
@@ -23,16 +23,20 @@ class JobService : BaseJobService() {
|
|||||||
override fun onStartJob(params: JobParameters): Boolean {
|
override fun onStartJob(params: JobParameters): Boolean {
|
||||||
val coroutineScope = CoroutineScope(Dispatchers.IO + job)
|
val coroutineScope = CoroutineScope(Dispatchers.IO + job)
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
svc.fetchUpdate()?.run {
|
doWork()
|
||||||
Info.remote = this
|
|
||||||
if (Info.env.isActive && BuildConfig.VERSION_CODE < magisk.versionCode)
|
|
||||||
Notifications.managerUpdate(this@JobService)
|
|
||||||
}
|
|
||||||
jobFinished(params, false)
|
jobFinished(params, false)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun doWork() {
|
||||||
|
svc.fetchUpdate()?.let {
|
||||||
|
Info.remote = it
|
||||||
|
if (Info.env.isActive && BuildConfig.VERSION_CODE < it.magisk.versionCode)
|
||||||
|
Notifications.updateAvailable(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStopJob(params: JobParameters): Boolean {
|
override fun onStopJob(params: JobParameters): Boolean {
|
||||||
job.cancel()
|
job.cancel()
|
||||||
return false
|
return false
|
||||||
|
@@ -5,6 +5,7 @@ import android.content.ContextWrapper
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.topjohnwu.magisk.core.base.BaseReceiver
|
import com.topjohnwu.magisk.core.base.BaseReceiver
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import com.topjohnwu.magisk.view.Shortcuts
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@@ -42,9 +43,16 @@ open class Receiver : BaseReceiver() {
|
|||||||
getUid(intent)?.let { rmPolicy(it) }
|
getUid(intent)?.let { rmPolicy(it) }
|
||||||
}
|
}
|
||||||
Intent.ACTION_PACKAGE_FULLY_REMOVED -> {
|
Intent.ACTION_PACKAGE_FULLY_REMOVED -> {
|
||||||
getPkg(intent)?.let { Shell.su("magisk --denylist rm $it").submit() }
|
getPkg(intent)?.let { Shell.cmd("magisk --denylist rm $it").submit() }
|
||||||
}
|
}
|
||||||
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setupDynamic(context)
|
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setupDynamic(context)
|
||||||
|
Intent.ACTION_MY_PACKAGE_REPLACED -> {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val installer = context.packageManager.getInstallerPackageName(context.packageName)
|
||||||
|
if (installer == context.packageName) {
|
||||||
|
Notifications.updateDone(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.core.base
|
package com.topjohnwu.magisk.core.base
|
||||||
|
|
||||||
|
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -9,11 +10,16 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
|
import com.topjohnwu.magisk.core.utils.RequestInstall
|
||||||
|
import com.topjohnwu.magisk.core.utils.UninstallPackage
|
||||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||||
import com.topjohnwu.magisk.core.wrap
|
import com.topjohnwu.magisk.core.wrap
|
||||||
import com.topjohnwu.magisk.ktx.reflectField
|
import com.topjohnwu.magisk.ktx.reflectField
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity() {
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -22,6 +28,10 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
permissionCallback?.invoke(it)
|
permissionCallback?.invoke(it)
|
||||||
permissionCallback = null
|
permissionCallback = null
|
||||||
}
|
}
|
||||||
|
private val requestInstall = registerForActivityResult(RequestInstall()) {
|
||||||
|
permissionCallback?.invoke(it)
|
||||||
|
permissionCallback = null
|
||||||
|
}
|
||||||
|
|
||||||
private var contentCallback: ((Uri) -> Unit)? = null
|
private var contentCallback: ((Uri) -> Unit)? = null
|
||||||
private val getContent = registerForActivityResult(GetContent()) {
|
private val getContent = registerForActivityResult(GetContent()) {
|
||||||
@@ -29,6 +39,11 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
contentCallback = null
|
contentCallback = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var uninstallLatch = CountDownLatch(1)
|
||||||
|
private val uninstallPkg = registerForActivityResult(UninstallPackage()) {
|
||||||
|
uninstallLatch.countDown()
|
||||||
|
}
|
||||||
|
|
||||||
override fun applyOverrideConfiguration(config: Configuration?) {
|
override fun applyOverrideConfiguration(config: Configuration?) {
|
||||||
// Force applying our preferred local
|
// Force applying our preferred local
|
||||||
config?.setLocale(currentLocale)
|
config?.setLocale(currentLocale)
|
||||||
@@ -57,7 +72,11 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
permissionCallback = callback
|
permissionCallback = callback
|
||||||
requestPermission.launch(permission)
|
if (permission == REQUEST_INSTALL_PACKAGES) {
|
||||||
|
requestInstall.launch(Unit)
|
||||||
|
} else {
|
||||||
|
requestPermission.launch(permission)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getContent(type: String, callback: (Uri) -> Unit) {
|
fun getContent(type: String, callback: (Uri) -> Unit) {
|
||||||
@@ -65,6 +84,13 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
getContent.launch(type)
|
getContent.launch(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
fun uninstallAndWait(pkg: String) {
|
||||||
|
uninstallLatch = CountDownLatch(1)
|
||||||
|
uninstallPkg.launch(pkg)
|
||||||
|
uninstallLatch.await(3, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
override fun recreate() {
|
override fun recreate() {
|
||||||
startActivity(Intent().setComponent(intent.component))
|
startActivity(Intent().setComponent(intent.component))
|
||||||
finish()
|
finish()
|
||||||
|
@@ -1,170 +1,186 @@
|
|||||||
package com.topjohnwu.magisk.core.download
|
package com.topjohnwu.magisk.core.download
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Notification
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.PendingIntent.*
|
import android.app.PendingIntent.*
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import androidx.core.net.toFile
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.ForegroundTracker
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.base.BaseService
|
import com.topjohnwu.magisk.core.*
|
||||||
import com.topjohnwu.magisk.core.intent
|
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||||
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||||
import com.topjohnwu.magisk.ktx.synchronized
|
import com.topjohnwu.magisk.ktx.copyAndClose
|
||||||
|
import com.topjohnwu.magisk.ktx.forEach
|
||||||
|
import com.topjohnwu.magisk.ktx.withStreams
|
||||||
|
import com.topjohnwu.magisk.ktx.writeTo
|
||||||
|
import com.topjohnwu.magisk.utils.APKInstall
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import com.topjohnwu.magisk.view.Notifications.mgr
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
class DownloadService : BaseService() {
|
class DownloadService : NotificationService() {
|
||||||
|
|
||||||
private val hasNotifications get() = notifications.isNotEmpty()
|
|
||||||
private val notifications = HashMap<Int, Notification.Builder>().synchronized()
|
|
||||||
private val job = Job()
|
private val job = Job()
|
||||||
|
|
||||||
val service get() = ServiceLocator.networkService
|
|
||||||
|
|
||||||
// -- Service overrides
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? = null
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
intent.getParcelableExtra<Subject>(SUBJECT_KEY)?.let { doDownload(it) }
|
intent.getParcelableExtra<Subject>(SUBJECT_KEY)?.let { download(it) }
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
|
||||||
super.onTaskRemoved(rootIntent)
|
|
||||||
notifications.forEach { mgr.cancel(it.key) }
|
|
||||||
notifications.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Download logic
|
private fun download(subject: Subject) {
|
||||||
|
|
||||||
private fun doDownload(subject: Subject) {
|
|
||||||
update(subject.notifyId)
|
update(subject.notifyId)
|
||||||
val coroutineScope = CoroutineScope(job + Dispatchers.IO)
|
val coroutineScope = CoroutineScope(job + Dispatchers.IO)
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
try {
|
try {
|
||||||
val stream = service.fetchFile(subject.url).toProgressStream(subject)
|
val stream = service.fetchFile(subject.url).toProgressStream(subject)
|
||||||
when (subject) {
|
when (subject) {
|
||||||
is Subject.Manager -> handleAPK(subject, stream)
|
is Subject.App -> handleApp(stream, subject)
|
||||||
else -> stream.toModule(subject.file, service.fetchInstaller().byteStream())
|
is Subject.Module -> handleModule(stream, subject.file)
|
||||||
}
|
}
|
||||||
if (ForegroundTracker.hasForeground) {
|
val activity = ActivityTracker.foreground
|
||||||
|
if (activity != null && subject.autoLaunch) {
|
||||||
remove(subject.notifyId)
|
remove(subject.notifyId)
|
||||||
subject.pendingIntent(this@DownloadService)?.send()
|
subject.pendingIntent(activity)?.send()
|
||||||
} else {
|
} else {
|
||||||
notifyFinish(subject)
|
notifyFinish(subject)
|
||||||
}
|
}
|
||||||
|
subject.postDownload?.invoke()
|
||||||
if (!hasNotifications)
|
if (!hasNotifications)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
} catch (e: IOException) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
notifyFail(subject)
|
notifyFail(subject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ResponseBody.toProgressStream(subject: Subject): InputStream {
|
private suspend fun handleApp(stream: InputStream, subject: Subject.App) {
|
||||||
val max = contentLength()
|
fun writeTee(output: OutputStream) {
|
||||||
val total = max.toFloat() / 1048576
|
val uri = MediaStoreUtils.getFile("${subject.title}.apk").uri
|
||||||
val id = subject.notifyId
|
val external = uri.outputStream()
|
||||||
|
stream.copyAndClose(TeeOutputStream(external, output))
|
||||||
|
}
|
||||||
|
|
||||||
update(id) { it.setContentTitle(subject.title) }
|
if (isRunningAsStub) {
|
||||||
|
val updateApk = StubApk.update(this)
|
||||||
|
try {
|
||||||
|
// Download full APK to stub update path
|
||||||
|
writeTee(updateApk.outputStream())
|
||||||
|
|
||||||
return ProgressInputStream(byteStream()) {
|
if (Info.stub!!.version < subject.stub.versionCode) {
|
||||||
val progress = it.toFloat() / 1048576
|
// Also upgrade stub
|
||||||
update(id) { notification ->
|
update(subject.notifyId) {
|
||||||
if (max > 0) {
|
it.setProgress(0, 0, true)
|
||||||
broadcast(progress / total, subject)
|
.setContentTitle(getString(R.string.hide_app_title))
|
||||||
notification
|
.setContentText("")
|
||||||
.setProgress(max.toInt(), it.toInt(), false)
|
}
|
||||||
.setContentText("%.2f / %.2f MB".format(progress, total))
|
|
||||||
|
// Download
|
||||||
|
val apk = subject.file.toFile()
|
||||||
|
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
|
||||||
|
|
||||||
|
// Patch and install
|
||||||
|
val session = APKInstall.startSession(this)
|
||||||
|
session.openStream(this).use {
|
||||||
|
val label = applicationInfo.nonLocalizedLabel
|
||||||
|
if (!HideAPK.patch(this, apk, it, packageName, label)) {
|
||||||
|
throw IOException("HideAPK patch error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apk.delete()
|
||||||
|
subject.intent = session.waitIntent()
|
||||||
} else {
|
} else {
|
||||||
broadcast(-1f, subject)
|
ActivityTracker.foreground?.let {
|
||||||
notification.setContentText("%.2f MB / ??".format(progress))
|
// Relaunch the process if we are foreground
|
||||||
|
StubApk.restartProcess(it)
|
||||||
|
} ?: run {
|
||||||
|
// Or else kill the current process after posting notification
|
||||||
|
subject.intent = Notifications.selfLaunchIntent(this)
|
||||||
|
subject.postDownload = { Runtime.getRuntime().exit(0) }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// If any error occurred, do not let stub load the new APK
|
||||||
|
updateApk.delete()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val session = APKInstall.startSession(this)
|
||||||
|
writeTee(session.openStream(this))
|
||||||
|
subject.intent = session.waitIntent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleModule(src: InputStream, file: Uri) {
|
||||||
|
val input = ZipInputStream(src.buffered())
|
||||||
|
val output = ZipOutputStream(file.outputStream().buffered())
|
||||||
|
|
||||||
|
withStreams(input, output) { zin, zout ->
|
||||||
|
zout.putNextEntry(ZipEntry("META-INF/"))
|
||||||
|
zout.putNextEntry(ZipEntry("META-INF/com/"))
|
||||||
|
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
|
||||||
|
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
|
||||||
|
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
|
||||||
|
assets.open("module_installer.sh").copyTo(zout)
|
||||||
|
|
||||||
|
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
|
||||||
|
zout.write("#MAGISK\n".toByteArray())
|
||||||
|
|
||||||
|
zin.forEach { entry ->
|
||||||
|
val path = entry.name
|
||||||
|
if (path.isNotEmpty() && !path.startsWith("META-INF")) {
|
||||||
|
zout.putNextEntry(ZipEntry(path))
|
||||||
|
if (!entry.isDirectory) {
|
||||||
|
zin.copyTo(zout)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Notification management
|
private class TeeOutputStream(
|
||||||
|
private val o1: OutputStream,
|
||||||
private fun notifyFail(subject: Subject) = finalNotify(subject.notifyId) {
|
private val o2: OutputStream
|
||||||
broadcast(-2f, subject)
|
) : OutputStream() {
|
||||||
it.setContentText(getString(R.string.download_file_error))
|
override fun write(b: Int) {
|
||||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
o1.write(b)
|
||||||
.setOngoing(false)
|
o2.write(b)
|
||||||
}
|
}
|
||||||
|
override fun write(b: ByteArray?, off: Int, len: Int) {
|
||||||
private fun notifyFinish(subject: Subject) = finalNotify(subject.notifyId) {
|
o1.write(b, off, len)
|
||||||
broadcast(1f, subject)
|
o2.write(b, off, len)
|
||||||
it.setContentIntent(subject.pendingIntent(this))
|
}
|
||||||
.setContentTitle(subject.title)
|
override fun close() {
|
||||||
.setContentText(getString(R.string.download_complete))
|
o1.close()
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
o2.close()
|
||||||
.setProgress(0, 0, false)
|
|
||||||
.setOngoing(false)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int {
|
|
||||||
val notification = remove(id)?.also(editor) ?: return -1
|
|
||||||
val newId = Notifications.nextId()
|
|
||||||
mgr.notify(newId, notification.build())
|
|
||||||
return newId
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun create() = Notifications.progress(this, "")
|
|
||||||
|
|
||||||
fun update(id: Int, editor: (Notification.Builder) -> Unit = {}) {
|
|
||||||
val wasEmpty = !hasNotifications
|
|
||||||
val notification = notifications.getOrPut(id, ::create).also(editor)
|
|
||||||
if (wasEmpty)
|
|
||||||
updateForeground()
|
|
||||||
else
|
|
||||||
mgr.notify(id, notification.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun remove(id: Int): Notification.Builder? {
|
|
||||||
val n = notifications.remove(id)?.also { updateForeground() }
|
|
||||||
mgr.cancel(id)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateForeground() {
|
|
||||||
if (hasNotifications) {
|
|
||||||
val (id, notification) = notifications.entries.first()
|
|
||||||
startForeground(id, notification.build())
|
|
||||||
} else {
|
|
||||||
stopForeground(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SUBJECT_KEY = "download_subject"
|
private const val SUBJECT_KEY = "subject"
|
||||||
private const val REQUEST_CODE = 1
|
private const val REQUEST_CODE = 1
|
||||||
|
|
||||||
private val progressBroadcast = MutableLiveData<Pair<Float, Subject>?>()
|
|
||||||
|
|
||||||
fun observeProgress(owner: LifecycleOwner, callback: (Float, Subject) -> Unit) {
|
fun observeProgress(owner: LifecycleOwner, callback: (Float, Subject) -> Unit) {
|
||||||
progressBroadcast.value = null
|
progressBroadcast.value = null
|
||||||
progressBroadcast.observe(owner) {
|
progressBroadcast.observe(owner) {
|
||||||
@@ -173,10 +189,6 @@ class DownloadService : BaseService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun broadcast(progress: Float, subject: Subject) {
|
|
||||||
progressBroadcast.postValue(progress to subject)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun intent(context: Context, subject: Subject) =
|
private fun intent(context: Context, subject: Subject) =
|
||||||
context.intent<DownloadService>().putExtra(SUBJECT_KEY, subject)
|
context.intent<DownloadService>().putExtra(SUBJECT_KEY, subject)
|
||||||
|
|
||||||
@@ -200,5 +212,4 @@ class DownloadService : BaseService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,77 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.download
|
|
||||||
|
|
||||||
import android.app.AlarmManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.core.content.getSystemService
|
|
||||||
import androidx.core.net.toFile
|
|
||||||
import com.topjohnwu.magisk.DynAPK
|
|
||||||
import com.topjohnwu.magisk.R
|
|
||||||
import com.topjohnwu.magisk.core.ForegroundTracker
|
|
||||||
import com.topjohnwu.magisk.core.Info
|
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
|
||||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
|
||||||
import com.topjohnwu.magisk.ktx.copyAndClose
|
|
||||||
import com.topjohnwu.magisk.ktx.writeTo
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun DownloadService.handleAPK(subject: Subject.Manager, stream: InputStream) {
|
|
||||||
fun write(output: OutputStream) {
|
|
||||||
val external = subject.externalFile.outputStream()
|
|
||||||
stream.copyAndClose(TeeOutputStream(external, output))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRunningAsStub) {
|
|
||||||
val apk = subject.file.toFile()
|
|
||||||
val id = subject.notifyId
|
|
||||||
write(DynAPK.update(this).outputStream())
|
|
||||||
if (Info.stub!!.version < subject.stub.versionCode) {
|
|
||||||
// Also upgrade stub
|
|
||||||
update(id) {
|
|
||||||
it.setProgress(0, 0, true)
|
|
||||||
.setContentTitle(getString(R.string.hide_app_title))
|
|
||||||
.setContentText("")
|
|
||||||
}
|
|
||||||
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
|
|
||||||
val patched = File(apk.parent, "patched.apk")
|
|
||||||
HideAPK.patch(this, apk, patched, packageName, applicationInfo.nonLocalizedLabel)
|
|
||||||
apk.delete()
|
|
||||||
patched.renameTo(apk)
|
|
||||||
} else {
|
|
||||||
val intent = packageManager.getLaunchIntentForPackage(packageName)
|
|
||||||
intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
//noinspection InlinedApi
|
|
||||||
val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
val pending = PendingIntent.getActivity(this, id, intent, flag)
|
|
||||||
if (ForegroundTracker.hasForeground) {
|
|
||||||
val alarm = getSystemService<AlarmManager>()
|
|
||||||
alarm!!.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pending)
|
|
||||||
}
|
|
||||||
stopSelf()
|
|
||||||
Runtime.getRuntime().exit(0)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
write(subject.file.outputStream())
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,38 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.download
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
|
||||||
import com.topjohnwu.magisk.ktx.forEach
|
|
||||||
import com.topjohnwu.magisk.ktx.withStreams
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
import java.util.zip.ZipOutputStream
|
|
||||||
|
|
||||||
fun InputStream.toModule(file: Uri, installer: InputStream) {
|
|
||||||
|
|
||||||
val input = ZipInputStream(buffered())
|
|
||||||
val output = ZipOutputStream(file.outputStream().buffered())
|
|
||||||
|
|
||||||
withStreams(input, output) { zin, zout ->
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
|
|
||||||
installer.copyTo(zout)
|
|
||||||
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
|
|
||||||
zout.write("#MAGISK\n".toByteArray(charset("UTF-8")))
|
|
||||||
|
|
||||||
zin.forEach { entry ->
|
|
||||||
val path = entry.name
|
|
||||||
if (path.isNotEmpty() && !path.startsWith("META-INF")) {
|
|
||||||
zout.putNextEntry(ZipEntry(path))
|
|
||||||
if (!entry.isDirectory) {
|
|
||||||
zin.copyTo(zout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,113 @@
|
|||||||
|
package com.topjohnwu.magisk.core.download
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.IBinder
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.core.base.BaseService
|
||||||
|
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
||||||
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.ktx.synchronized
|
||||||
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
|
import okhttp3.ResponseBody
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
open class NotificationService : BaseService() {
|
||||||
|
|
||||||
|
private val notifications = HashMap<Int, Notification.Builder>().synchronized()
|
||||||
|
protected val hasNotifications get() = notifications.isNotEmpty()
|
||||||
|
|
||||||
|
protected val service get() = ServiceLocator.networkService
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? = null
|
||||||
|
|
||||||
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||||
|
super.onTaskRemoved(rootIntent)
|
||||||
|
notifications.forEach { Notifications.mgr.cancel(it.key) }
|
||||||
|
notifications.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun ResponseBody.toProgressStream(subject: Subject): InputStream {
|
||||||
|
val max = contentLength()
|
||||||
|
val total = max.toFloat() / 1048576
|
||||||
|
val id = subject.notifyId
|
||||||
|
|
||||||
|
update(id) { it.setContentTitle(subject.title) }
|
||||||
|
|
||||||
|
return ProgressInputStream(byteStream()) {
|
||||||
|
val progress = it.toFloat() / 1048576
|
||||||
|
update(id) { notification ->
|
||||||
|
if (max > 0) {
|
||||||
|
broadcast(progress / total, subject)
|
||||||
|
notification
|
||||||
|
.setProgress(max.toInt(), it.toInt(), false)
|
||||||
|
.setContentText("%.2f / %.2f MB".format(progress, total))
|
||||||
|
} else {
|
||||||
|
broadcast(-1f, subject)
|
||||||
|
notification.setContentText("%.2f MB / ??".format(progress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int {
|
||||||
|
val notification = remove(id)?.also(editor) ?: return -1
|
||||||
|
val newId = Notifications.nextId()
|
||||||
|
Notifications.mgr.notify(newId, notification.build())
|
||||||
|
return newId
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun notifyFail(subject: Subject) = finalNotify(subject.notifyId) {
|
||||||
|
broadcast(-2f, subject)
|
||||||
|
it.setContentText(getString(R.string.download_file_error))
|
||||||
|
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||||
|
.setOngoing(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun notifyFinish(subject: Subject) = finalNotify(subject.notifyId) {
|
||||||
|
broadcast(1f, subject)
|
||||||
|
it.setContentTitle(subject.title)
|
||||||
|
.setContentText(getString(R.string.download_complete))
|
||||||
|
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
.setProgress(0, 0, false)
|
||||||
|
.setOngoing(false)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
subject.pendingIntent(this)?.let { intent -> it.setContentIntent(intent) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun create() = Notifications.progress(this, "")
|
||||||
|
|
||||||
|
private fun updateForeground() {
|
||||||
|
if (hasNotifications) {
|
||||||
|
val (id, notification) = notifications.entries.first()
|
||||||
|
startForeground(id, notification.build())
|
||||||
|
} else {
|
||||||
|
stopForeground(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun update(id: Int, editor: (Notification.Builder) -> Unit = {}) {
|
||||||
|
val wasEmpty = !hasNotifications
|
||||||
|
val notification = notifications.getOrPut(id, ::create).also(editor)
|
||||||
|
if (wasEmpty)
|
||||||
|
updateForeground()
|
||||||
|
else
|
||||||
|
Notifications.mgr.notify(id, notification.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun remove(id: Int): Notification.Builder? {
|
||||||
|
val n = notifications.remove(id)?.also { updateForeground() }
|
||||||
|
Notifications.mgr.cancel(id)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
protected val progressBroadcast = MutableLiveData<Pair<Float, Subject>?>()
|
||||||
|
|
||||||
|
private fun broadcast(progress: Float, subject: Subject) {
|
||||||
|
progressBroadcast.postValue(progress to subject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,6 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.core.net.toFile
|
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.model.MagiskJson
|
import com.topjohnwu.magisk.core.model.MagiskJson
|
||||||
@@ -16,7 +15,6 @@ import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
|||||||
import com.topjohnwu.magisk.di.AppContext
|
import com.topjohnwu.magisk.di.AppContext
|
||||||
import com.topjohnwu.magisk.ktx.cachedFile
|
import com.topjohnwu.magisk.ktx.cachedFile
|
||||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||||
import com.topjohnwu.magisk.utils.APKInstall
|
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@@ -34,6 +32,8 @@ sealed class Subject : Parcelable {
|
|||||||
abstract val file: Uri
|
abstract val file: Uri
|
||||||
abstract val title: String
|
abstract val title: String
|
||||||
abstract val notifyId: Int
|
abstract val notifyId: Int
|
||||||
|
open val autoLaunch: Boolean get() = true
|
||||||
|
open val postDownload: (() -> Unit)? get() = null
|
||||||
|
|
||||||
abstract fun pendingIntent(context: Context): PendingIntent?
|
abstract fun pendingIntent(context: Context): PendingIntent?
|
||||||
|
|
||||||
@@ -45,20 +45,19 @@ sealed class Subject : Parcelable {
|
|||||||
) : Subject() {
|
) : Subject() {
|
||||||
override val url: String get() = module.zipUrl
|
override val url: String get() = module.zipUrl
|
||||||
override val title: String get() = module.downloadFilename
|
override val title: String get() = module.downloadFilename
|
||||||
|
override val autoLaunch: Boolean get() = action == Action.Flash
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
override val file by lazy {
|
override val file by lazy {
|
||||||
MediaStoreUtils.getFile(title).uri
|
MediaStoreUtils.getFile(title).uri
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pendingIntent(context: Context) = when (action) {
|
override fun pendingIntent(context: Context) =
|
||||||
Action.Flash -> FlashFragment.installIntent(context, file)
|
FlashFragment.installIntent(context, file)
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
class Manager(
|
class App(
|
||||||
private val json: MagiskJson = Info.remote.magisk,
|
private val json: MagiskJson = Info.remote.magisk,
|
||||||
val stub: StubJson = Info.remote.stub,
|
val stub: StubJson = Info.remote.stub,
|
||||||
override val notifyId: Int = Notifications.nextId()
|
override val notifyId: Int = Notifications.nextId()
|
||||||
@@ -71,14 +70,12 @@ sealed class Subject : Parcelable {
|
|||||||
cachedFile("manager.apk")
|
cachedFile("manager.apk")
|
||||||
}
|
}
|
||||||
|
|
||||||
val externalFile get() = MediaStoreUtils.getFile("$title.apk").uri
|
@IgnoredOnParcel
|
||||||
|
override var postDownload: (() -> Unit)? = null
|
||||||
|
|
||||||
override fun pendingIntent(context: Context): PendingIntent {
|
@IgnoredOnParcel
|
||||||
val receiver = APKInstall.register(context, null, null)
|
var intent: Intent? = null
|
||||||
APKInstall.installapk(context, file.toFile())
|
override fun pendingIntent(context: Context) = intent?.toPending(context)
|
||||||
val intent = receiver.waitIntent() ?: Intent()
|
|
||||||
return intent.toPending(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
|
@@ -18,7 +18,7 @@ class Query(private val _query: String) {
|
|||||||
|
|
||||||
suspend inline fun <R : Any> query(crossinline mapper: (Map<String, String>) -> R?): List<R> =
|
suspend inline fun <R : Any> query(crossinline mapper: (Map<String, String>) -> R?): List<R> =
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
Shell.su(query).await().out.map { line ->
|
Shell.cmd(query).await().out.map { line ->
|
||||||
async {
|
async {
|
||||||
line.split("\\|".toRegex())
|
line.split("\\|".toRegex())
|
||||||
.map { it.split("=", limit = 2) }
|
.map { it.split("=", limit = 2) }
|
||||||
@@ -32,7 +32,7 @@ class Query(private val _query: String) {
|
|||||||
|
|
||||||
suspend inline fun query() = query { it }
|
suspend inline fun query() = query { it }
|
||||||
|
|
||||||
suspend inline fun commit() = Shell.su(query).to(null).await()
|
suspend inline fun commit() = Shell.cmd(query).to(null).await()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Delete : Query.Builder {
|
class Delete : Query.Builder {
|
||||||
|
@@ -46,15 +46,15 @@ data class LocalModule(
|
|||||||
if (enable) {
|
if (enable) {
|
||||||
disableFile.delete()
|
disableFile.delete()
|
||||||
if (Const.Version.atLeast_21_2())
|
if (Const.Version.atLeast_21_2())
|
||||||
Shell.su("copy_sepolicy_rules").submit()
|
Shell.cmd("copy_sepolicy_rules").submit()
|
||||||
else
|
else
|
||||||
Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
|
Shell.cmd("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
|
||||||
} else {
|
} else {
|
||||||
!disableFile.createNewFile()
|
!disableFile.createNewFile()
|
||||||
if (Const.Version.atLeast_21_2())
|
if (Const.Version.atLeast_21_2())
|
||||||
Shell.su("copy_sepolicy_rules").submit()
|
Shell.cmd("copy_sepolicy_rules").submit()
|
||||||
else
|
else
|
||||||
Shell.su("rm -rf $dir").submit()
|
Shell.cmd("rm -rf $dir").submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,15 +65,15 @@ data class LocalModule(
|
|||||||
if (updateFile.exists()) return
|
if (updateFile.exists()) return
|
||||||
removeFile.createNewFile()
|
removeFile.createNewFile()
|
||||||
if (Const.Version.atLeast_21_2())
|
if (Const.Version.atLeast_21_2())
|
||||||
Shell.su("copy_sepolicy_rules").submit()
|
Shell.cmd("copy_sepolicy_rules").submit()
|
||||||
else
|
else
|
||||||
Shell.su("rm -rf $PERSIST/$id").submit()
|
Shell.cmd("rm -rf $PERSIST/$id").submit()
|
||||||
} else {
|
} else {
|
||||||
removeFile.delete()
|
removeFile.delete()
|
||||||
if (Const.Version.atLeast_21_2())
|
if (Const.Version.atLeast_21_2())
|
||||||
Shell.su("copy_sepolicy_rules").submit()
|
Shell.cmd("copy_sepolicy_rules").submit()
|
||||||
else
|
else
|
||||||
Shell.su("cp -af $ruleFile $PERSIST/$id").submit()
|
Shell.cmd("cp -af $ruleFile $PERSIST/$id").submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ data class LocalModule(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
runCatching {
|
runCatching {
|
||||||
parseProps(Shell.su("dos2unix < $path/module.prop").exec().out)
|
parseProps(Shell.cmd("dos2unix < $path/module.prop").exec().out)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
|
@@ -36,7 +36,7 @@ class SuRequestHandler(
|
|||||||
|
|
||||||
// Never allow com.topjohnwu.magisk (could be malware)
|
// Never allow com.topjohnwu.magisk (could be malware)
|
||||||
if (policy.packageName == BuildConfig.APPLICATION_ID) {
|
if (policy.packageName == BuildConfig.APPLICATION_ID) {
|
||||||
Shell.su("(pm uninstall ${BuildConfig.APPLICATION_ID})& >/dev/null 2>&1").exec()
|
Shell.cmd("(pm uninstall ${BuildConfig.APPLICATION_ID})& >/dev/null 2>&1").exec()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -63,7 +63,7 @@ open class FlashZip(
|
|||||||
|
|
||||||
console.add("- Installing ${mUri.displayName}")
|
console.add("- Installing ${mUri.displayName}")
|
||||||
|
|
||||||
return Shell.su("sh $installDir/update-binary dummy 1 \'$zipFile\'")
|
return Shell.cmd("sh $installDir/update-binary dummy 1 \'$zipFile\'")
|
||||||
.to(console, logs).exec().isSuccess
|
.to(console, logs).exec().isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ open class FlashZip(
|
|||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
false
|
false
|
||||||
} finally {
|
} finally {
|
||||||
Shell.su("cd /", "rm -rf $installDir ${Const.TMPDIR}").submit()
|
Shell.cmd("cd /", "rm -rf $installDir ${Const.TMPDIR}").submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,8 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||||
import com.topjohnwu.magisk.DynAPK
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
@@ -21,18 +21,18 @@ import com.topjohnwu.magisk.utils.APKInstall
|
|||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Runnable
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
||||||
object HideAPK {
|
object HideAPK {
|
||||||
|
|
||||||
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
||||||
private const val ALPHADOTS = "$ALPHA....."
|
private const val ALPHADOTS = "$ALPHA....."
|
||||||
private const val APP_NAME = "Magisk"
|
|
||||||
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||||
|
|
||||||
// Some arbitrary limit
|
// Some arbitrary limit
|
||||||
@@ -65,27 +65,29 @@ object HideAPK {
|
|||||||
|
|
||||||
fun patch(
|
fun patch(
|
||||||
context: Context,
|
context: Context,
|
||||||
apk: File, out: File,
|
apk: File, out: OutputStream,
|
||||||
pkg: String, label: CharSequence
|
pkg: String, label: CharSequence
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
val info = context.packageManager.getPackageArchiveInfo(apk.path, 0) ?: return false
|
||||||
|
val name = info.applicationInfo.nonLocalizedLabel.toString()
|
||||||
try {
|
try {
|
||||||
val jar = JarMap.open(apk, true)
|
JarMap.open(apk, true).use { jar ->
|
||||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||||
val xml = AXML(jar.getRawData(je))
|
val xml = AXML(jar.getRawData(je))
|
||||||
|
|
||||||
if (!xml.findAndPatch(APPLICATION_ID to pkg, APP_NAME to label.toString()))
|
if (!xml.findAndPatch(APPLICATION_ID to pkg, name to label.toString()))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
// Write apk changes
|
// Write apk changes
|
||||||
jar.getOutputStream(je).write(xml.bytes)
|
jar.getOutputStream(je).use { it.write(xml.bytes) }
|
||||||
val keys = Keygen(context)
|
val keys = Keygen(context)
|
||||||
SignApk.sign(keys.cert, keys.key, jar, FileOutputStream(out))
|
SignApk.sign(keys.cert, keys.key, jar, out)
|
||||||
|
return true
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchApp(activity: Activity, pkg: String) {
|
private fun launchApp(activity: Activity, pkg: String) {
|
||||||
@@ -100,7 +102,7 @@ object HideAPK {
|
|||||||
activity.finish()
|
activity.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun patchAndHide(activity: Activity, label: String): Boolean {
|
private suspend fun patchAndHide(activity: Activity, label: String, onFailure: Runnable): Boolean {
|
||||||
val stub = File(activity.cacheDir, "stub.apk")
|
val stub = File(activity.cacheDir, "stub.apk")
|
||||||
try {
|
try {
|
||||||
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
|
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
|
||||||
@@ -108,26 +110,28 @@ object HideAPK {
|
|||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
stub.createNewFile()
|
stub.createNewFile()
|
||||||
val cmd = "\$MAGISKBIN/magiskinit -x manager ${stub.path}"
|
val cmd = "\$MAGISKBIN/magiskinit -x manager ${stub.path}"
|
||||||
if (!Shell.su(cmd).exec().isSuccess) return false
|
if (!Shell.cmd(cmd).exec().isSuccess)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new random package name and signature
|
// Generate a new random package name and signature
|
||||||
val repack = File(activity.cacheDir, "patched.apk")
|
|
||||||
val pkg = genPackageName()
|
val pkg = genPackageName()
|
||||||
Config.keyStoreRaw = ""
|
Config.keyStoreRaw = ""
|
||||||
|
|
||||||
if (!patch(activity, stub, repack, pkg, label))
|
|
||||||
return false
|
|
||||||
|
|
||||||
// Install and auto launch app
|
// Install and auto launch app
|
||||||
val receiver = APKInstall.register(activity, pkg) {
|
val session = APKInstall.startSession(activity, pkg, onFailure) {
|
||||||
launchApp(activity, pkg)
|
launchApp(activity, pkg)
|
||||||
}
|
}
|
||||||
val cmd = "adb_pm_install $repack ${activity.applicationInfo.uid}"
|
try {
|
||||||
if (!Shell.su(cmd).exec().isSuccess) {
|
val success = session.openStream(activity).use {
|
||||||
APKInstall.installapk(activity, repack)
|
patch(activity, stub, it, pkg, label)
|
||||||
receiver.waitIntent()?.let { activity.startActivity(it) }
|
}
|
||||||
|
if (!success) return false
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Timber.e(e)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
session.waitIntent()?.let { activity.startActivity(it) } ?: return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,33 +143,43 @@ object HideAPK {
|
|||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
val result = withContext(Dispatchers.IO) {
|
val onFailure = Runnable {
|
||||||
patchAndHide(activity, label)
|
|
||||||
}
|
|
||||||
if (!result) {
|
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
Utils.toast(R.string.failure, Toast.LENGTH_LONG)
|
Utils.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
val success = withContext(Dispatchers.IO) {
|
||||||
|
patchAndHide(activity, label, onFailure)
|
||||||
|
}
|
||||||
|
if (!success) onFailure.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun restore(activity: Activity) {
|
suspend fun restore(activity: Activity) {
|
||||||
val dialog = android.app.ProgressDialog(activity).apply {
|
val dialog = android.app.ProgressDialog(activity).apply {
|
||||||
setTitle(activity.getString(R.string.restore_img_msg))
|
setTitle(activity.getString(R.string.restore_img_msg))
|
||||||
isIndeterminate = true
|
isIndeterminate = true
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
val apk = DynAPK.current(activity)
|
val onFailure = Runnable {
|
||||||
val receiver = APKInstall.register(activity, APPLICATION_ID) {
|
dialog.dismiss()
|
||||||
|
Utils.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
val apk = StubApk.current(activity)
|
||||||
|
val session = APKInstall.startSession(activity, APPLICATION_ID, onFailure) {
|
||||||
launchApp(activity, APPLICATION_ID)
|
launchApp(activity, APPLICATION_ID)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
val cmd = "adb_pm_install $apk ${activity.applicationInfo.uid}"
|
val success = withContext(Dispatchers.IO) {
|
||||||
Shell.su(cmd).submit(Shell.EXECUTOR) { ret ->
|
try {
|
||||||
if (ret.isSuccess) return@submit
|
session.install(activity, apk)
|
||||||
APKInstall.installapk(activity, apk)
|
} catch (e: IOException) {
|
||||||
receiver.waitIntent()?.let { activity.startActivity(it) }
|
Timber.e(e)
|
||||||
|
return@withContext false
|
||||||
|
}
|
||||||
|
session.waitIntent()?.let { activity.startActivity(it) } ?: return@withContext false
|
||||||
|
return@withContext true
|
||||||
}
|
}
|
||||||
|
if (!success) onFailure.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,8 @@ import android.widget.Toast
|
|||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.os.postDelayed
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.DynAPK
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.*
|
import com.topjohnwu.magisk.core.*
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||||
@@ -93,7 +93,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
try {
|
try {
|
||||||
// Extract binaries
|
// Extract binaries
|
||||||
if (isRunningAsStub) {
|
if (isRunningAsStub) {
|
||||||
val zf = ZipFile(DynAPK.current(context))
|
val zf = ZipFile(StubApk.current(context))
|
||||||
|
|
||||||
// Also extract magisk32 on non 64-bit only 64-bit devices
|
// Also extract magisk32 on non 64-bit only 64-bit devices
|
||||||
val is32lib = Const.CPU_ABI_32?.let {
|
val is32lib = Const.CPU_ABI_32?.let {
|
||||||
@@ -452,7 +452,7 @@ abstract class MagiskInstaller(
|
|||||||
if (success) {
|
if (success) {
|
||||||
console.add("- All done!")
|
console.add("- All done!")
|
||||||
} else {
|
} else {
|
||||||
Shell.sh("rm -rf $installDir").submit()
|
Shell.cmd("rm -rf $installDir").submit()
|
||||||
console.add("! Installation failed")
|
console.add("! Installation failed")
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
@@ -497,7 +497,7 @@ abstract class MagiskInstaller(
|
|||||||
val success = super.exec()
|
val success = super.exec()
|
||||||
if (success) {
|
if (success) {
|
||||||
UiThreadHandler.handler.postDelayed(3000) {
|
UiThreadHandler.handler.postDelayed(3000) {
|
||||||
Shell.su("pm uninstall ${context.packageName}").exec()
|
Shell.cmd("pm uninstall ${context.packageName}").exec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
|
@@ -0,0 +1,34 @@
|
|||||||
|
package com.topjohnwu.magisk.core.utils
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
|
||||||
|
class RequestInstall : ActivityResultContract<Unit, Boolean>() {
|
||||||
|
|
||||||
|
@TargetApi(26)
|
||||||
|
override fun createIntent(context: Context, input: Unit): Intent {
|
||||||
|
// This will only be called on API 26+
|
||||||
|
return Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
|
||||||
|
.setData(Uri.parse("package:${context.packageName}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?) =
|
||||||
|
resultCode == Activity.RESULT_OK
|
||||||
|
|
||||||
|
override fun getSynchronousResult(
|
||||||
|
context: Context,
|
||||||
|
input: Unit
|
||||||
|
): SynchronousResult<Boolean>? {
|
||||||
|
if (Build.VERSION.SDK_INT < 26)
|
||||||
|
return SynchronousResult(true)
|
||||||
|
if (context.packageManager.canRequestPackageInstalls())
|
||||||
|
return SynchronousResult(true)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import android.content.Intent
|
|||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.ipc.RootService
|
import com.topjohnwu.superuser.ipc.RootService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
@@ -12,11 +13,7 @@ import kotlin.system.exitProcess
|
|||||||
|
|
||||||
class RootRegistry(stub: Any?) : RootService() {
|
class RootRegistry(stub: Any?) : RootService() {
|
||||||
|
|
||||||
constructor() : this(null)
|
constructor() : this(null) {
|
||||||
|
|
||||||
private val className: String? = stub?.javaClass?.name
|
|
||||||
|
|
||||||
init {
|
|
||||||
// Always log full stack trace with Timber
|
// Always log full stack trace with Timber
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
Thread.setDefaultUncaughtExceptionHandler { _, e ->
|
Thread.setDefaultUncaughtExceptionHandler { _, e ->
|
||||||
@@ -25,6 +22,8 @@ class RootRegistry(stub: Any?) : RootService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val className: String? = stub?.javaClass?.name
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder {
|
override fun onBind(intent: Intent): IBinder {
|
||||||
// TODO: PLACEHOLDER
|
// TODO: PLACEHOLDER
|
||||||
return Binder()
|
return Binder()
|
||||||
@@ -50,6 +49,6 @@ class RootRegistry(stub: Any?) : RootService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var bindTask: Runnable? = null
|
var bindTask: Shell.Task? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
package com.topjohnwu.magisk.core.utils
|
package com.topjohnwu.magisk.core.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.topjohnwu.magisk.DynAPK
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
@@ -19,7 +19,7 @@ import java.util.jar.JarFile
|
|||||||
class ShellInit : Shell.Initializer() {
|
class ShellInit : Shell.Initializer() {
|
||||||
override fun onInit(context: Context, shell: Shell): Boolean {
|
override fun onInit(context: Context, shell: Shell): Boolean {
|
||||||
if (shell.isRoot) {
|
if (shell.isRoot) {
|
||||||
RootRegistry.bindTask?.run()
|
RootRegistry.bindTask?.let { shell.execTask(it) }
|
||||||
RootRegistry.bindTask = null
|
RootRegistry.bindTask = null
|
||||||
}
|
}
|
||||||
shell.newJob().apply {
|
shell.newJob().apply {
|
||||||
@@ -29,7 +29,7 @@ class ShellInit : Shell.Initializer() {
|
|||||||
if (isRunningAsStub) {
|
if (isRunningAsStub) {
|
||||||
if (!shell.isRoot)
|
if (!shell.isRoot)
|
||||||
return true
|
return true
|
||||||
val jar = JarFile(DynAPK.current(context))
|
val jar = JarFile(StubApk.current(context))
|
||||||
val bb = jar.getJarEntry("lib/${Const.CPU_ABI}/libbusybox.so")
|
val bb = jar.getJarEntry("lib/${Const.CPU_ABI}/libbusybox.so")
|
||||||
localBB = context.deviceProtectedContext.cachedFile("busybox")
|
localBB = context.deviceProtectedContext.cachedFile("busybox")
|
||||||
localBB.delete()
|
localBB.delete()
|
||||||
@@ -42,7 +42,7 @@ class ShellInit : Shell.Initializer() {
|
|||||||
if (shell.isRoot) {
|
if (shell.isRoot) {
|
||||||
add("export MAGISKTMP=\$(magisk --path)/.magisk")
|
add("export MAGISKTMP=\$(magisk --path)/.magisk")
|
||||||
// Test if we can properly execute stuff in /data
|
// Test if we can properly execute stuff in /data
|
||||||
Info.noDataExec = !shell.newJob().add("$localBB true").exec().isSuccess
|
Info.noDataExec = !shell.newJob().add("$localBB sh -c \"$localBB true\"").exec().isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Info.noDataExec) {
|
if (Info.noDataExec) {
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.topjohnwu.magisk.core.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
|
||||||
|
class UninstallPackage : ActivityResultContract<String, Boolean>() {
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun createIntent(context: Context, input: String): Intent {
|
||||||
|
val uri = Uri.Builder().scheme("package").opaquePart(input).build()
|
||||||
|
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
|
||||||
|
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?) =
|
||||||
|
resultCode == Activity.RESULT_OK
|
||||||
|
}
|
@@ -38,8 +38,7 @@ fun InputStream.unzip(folder: File, path: String, junkPath: Boolean) {
|
|||||||
}
|
}
|
||||||
SuFileOutputStream.open(dest).use { out -> zin.copyTo(out) }
|
SuFileOutputStream.open(dest).use { out -> zin.copyTo(out) }
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
e.printStackTrace()
|
throw IOException(e)
|
||||||
throw e
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,26 +6,16 @@ import com.topjohnwu.magisk.core.model.UpdateInfo
|
|||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
|
||||||
private const val REVISION = "revision"
|
|
||||||
private const val BRANCH = "branch"
|
private const val BRANCH = "branch"
|
||||||
private const val REPO = "repo"
|
private const val REPO = "repo"
|
||||||
private const val FILE = "file"
|
private const val FILE = "file"
|
||||||
|
|
||||||
const val MAGISK_MAIN = "topjohnwu/Magisk"
|
|
||||||
|
|
||||||
interface GithubPageServices {
|
interface GithubPageServices {
|
||||||
|
|
||||||
@GET("{$FILE}")
|
@GET("{$FILE}")
|
||||||
suspend fun fetchUpdateJSON(@Path(FILE) file: String): UpdateInfo
|
suspend fun fetchUpdateJSON(@Path(FILE) file: String): UpdateInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JSDelivrServices {
|
|
||||||
|
|
||||||
@GET("$MAGISK_MAIN@{$REVISION}/scripts/module_installer.sh")
|
|
||||||
@Streaming
|
|
||||||
suspend fun fetchInstaller(@Path(REVISION) revision: String): ResponseBody
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RawServices {
|
interface RawServices {
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@@ -29,9 +29,9 @@ class LogRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Info.env.isActive) {
|
if (Info.env.isActive) {
|
||||||
Shell.su("cat ${Const.MAGISK_LOG} || logcat -d -s Magisk").to(list).await()
|
Shell.cmd("cat ${Const.MAGISK_LOG} || logcat -d -s Magisk").to(list).await()
|
||||||
} else {
|
} else {
|
||||||
Shell.sh("logcat -d").to(list).await()
|
Shell.cmd("logcat -d").to(list).await()
|
||||||
}
|
}
|
||||||
return list.buf.toString()
|
return list.buf.toString()
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ class LogRepository(
|
|||||||
suspend fun clearLogs() = logDao.deleteAll()
|
suspend fun clearLogs() = logDao.deleteAll()
|
||||||
|
|
||||||
fun clearMagiskLogs(cb: (Shell.Result) -> Unit) =
|
fun clearMagiskLogs(cb: (Shell.Result) -> Unit) =
|
||||||
Shell.su("echo -n > ${Const.MAGISK_LOG}").submit(cb)
|
Shell.cmd("echo -n > ${Const.MAGISK_LOG}").submit(cb)
|
||||||
|
|
||||||
suspend fun insert(log: SuLog) = logDao.insert(log)
|
suspend fun insert(log: SuLog) = logDao.insert(log)
|
||||||
|
|
||||||
|
@@ -7,7 +7,9 @@ import com.topjohnwu.magisk.core.Config.Value.CUSTOM_CHANNEL
|
|||||||
import com.topjohnwu.magisk.core.Config.Value.DEFAULT_CHANNEL
|
import com.topjohnwu.magisk.core.Config.Value.DEFAULT_CHANNEL
|
||||||
import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL
|
import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.data.network.*
|
import com.topjohnwu.magisk.data.network.GithubApiServices
|
||||||
|
import com.topjohnwu.magisk.data.network.GithubPageServices
|
||||||
|
import com.topjohnwu.magisk.data.network.RawServices
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -15,7 +17,6 @@ import java.io.IOException
|
|||||||
class NetworkService(
|
class NetworkService(
|
||||||
private val pages: GithubPageServices,
|
private val pages: GithubPageServices,
|
||||||
private val raw: RawServices,
|
private val raw: RawServices,
|
||||||
private val jsd: JSDelivrServices,
|
|
||||||
private val api: GithubApiServices
|
private val api: GithubApiServices
|
||||||
) {
|
) {
|
||||||
suspend fun fetchUpdate() = safe {
|
suspend fun fetchUpdate() = safe {
|
||||||
@@ -59,13 +60,7 @@ class NetworkService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch files
|
// Fetch files
|
||||||
suspend fun fetchInstaller() = wrap {
|
|
||||||
val sha = fetchMainVersion()
|
|
||||||
jsd.fetchInstaller(sha)
|
|
||||||
}
|
|
||||||
suspend fun fetchFile(url: String) = wrap { raw.fetchFile(url) }
|
suspend fun fetchFile(url: String) = wrap { raw.fetchFile(url) }
|
||||||
suspend fun fetchString(url: String) = wrap { raw.fetchString(url) }
|
suspend fun fetchString(url: String) = wrap { raw.fetchString(url) }
|
||||||
suspend fun fetchModuleJson(url: String) = wrap { raw.fetchModuleJson(url) }
|
suspend fun fetchModuleJson(url: String) = wrap { raw.fetchModuleJson(url) }
|
||||||
|
|
||||||
private suspend fun fetchMainVersion() = api.fetchBranch(MAGISK_MAIN, "master").commit.sha
|
|
||||||
}
|
}
|
||||||
|
@@ -47,21 +47,20 @@ object ServiceLocator {
|
|||||||
NetworkService(
|
NetworkService(
|
||||||
createApiService(retrofit, Const.Url.GITHUB_PAGE_URL),
|
createApiService(retrofit, Const.Url.GITHUB_PAGE_URL),
|
||||||
createApiService(retrofit, Const.Url.GITHUB_RAW_URL),
|
createApiService(retrofit, Const.Url.GITHUB_RAW_URL),
|
||||||
createApiService(retrofit, Const.Url.JS_DELIVR_URL),
|
|
||||||
createApiService(retrofit, Const.Url.GITHUB_API_URL)
|
createApiService(retrofit, Const.Url.GITHUB_API_URL)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object VMFactory : ViewModelProvider.Factory {
|
object VMFactory : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel?> create(clz: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return when (clz) {
|
return when (modelClass) {
|
||||||
HomeViewModel::class.java -> HomeViewModel(networkService)
|
HomeViewModel::class.java -> HomeViewModel(networkService)
|
||||||
LogViewModel::class.java -> LogViewModel(logRepo)
|
LogViewModel::class.java -> LogViewModel(logRepo)
|
||||||
SuperuserViewModel::class.java -> SuperuserViewModel(policyDB)
|
SuperuserViewModel::class.java -> SuperuserViewModel(policyDB)
|
||||||
InstallViewModel::class.java -> InstallViewModel(networkService)
|
InstallViewModel::class.java -> InstallViewModel(networkService)
|
||||||
SuRequestViewModel::class.java -> SuRequestViewModel(policyDB, timeoutPrefs)
|
SuRequestViewModel::class.java -> SuRequestViewModel(policyDB, timeoutPrefs)
|
||||||
else -> clz.newInstance()
|
else -> modelClass.newInstance()
|
||||||
} as T
|
} as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +68,7 @@ object ServiceLocator {
|
|||||||
|
|
||||||
inline fun <reified VM : ViewModel> ViewModelStoreOwner.viewModel() =
|
inline fun <reified VM : ViewModel> ViewModelStoreOwner.viewModel() =
|
||||||
lazy(LazyThreadSafetyMode.NONE) {
|
lazy(LazyThreadSafetyMode.NONE) {
|
||||||
ViewModelProvider(this, ServiceLocator.VMFactory).get(VM::class.java)
|
ViewModelProvider(this, ServiceLocator.VMFactory)[VM::class.java]
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSuLogDatabase(context: Context) =
|
private fun createSuLogDatabase(context: Context) =
|
||||||
|
@@ -20,7 +20,7 @@ object RebootEvent {
|
|||||||
R.id.action_reboot_bootloader -> systemReboot("bootloader")
|
R.id.action_reboot_bootloader -> systemReboot("bootloader")
|
||||||
R.id.action_reboot_download -> systemReboot("download")
|
R.id.action_reboot_download -> systemReboot("download")
|
||||||
R.id.action_reboot_edl -> systemReboot("edl")
|
R.id.action_reboot_edl -> systemReboot("edl")
|
||||||
R.id.action_reboot_recovery -> Shell.su("/system/bin/reboot recovery").submit()
|
R.id.action_reboot_recovery -> Shell.cmd("/system/bin/reboot recovery").submit()
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.events
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import com.topjohnwu.magisk.arch.ActivityExecutor
|
|
||||||
import com.topjohnwu.magisk.arch.UIActivity
|
|
||||||
import com.topjohnwu.magisk.arch.ViewEvent
|
|
||||||
import com.topjohnwu.magisk.utils.TextHolder
|
|
||||||
import com.topjohnwu.magisk.utils.asText
|
|
||||||
|
|
||||||
class SnackbarEvent constructor(
|
|
||||||
private val msg: TextHolder,
|
|
||||||
private val length: Int = Snackbar.LENGTH_SHORT,
|
|
||||||
private val builder: Snackbar.() -> Unit = {}
|
|
||||||
) : ViewEvent(), ActivityExecutor {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@StringRes res: Int,
|
|
||||||
length: Int = Snackbar.LENGTH_SHORT,
|
|
||||||
builder: Snackbar.() -> Unit = {}
|
|
||||||
) : this(res.asText(), length, builder)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
msg: String,
|
|
||||||
length: Int = Snackbar.LENGTH_SHORT,
|
|
||||||
builder: Snackbar.() -> Unit = {}
|
|
||||||
) : this(msg.asText(), length, builder)
|
|
||||||
|
|
||||||
|
|
||||||
private fun snackbar(
|
|
||||||
view: View,
|
|
||||||
anchor: View?,
|
|
||||||
message: String,
|
|
||||||
length: Int,
|
|
||||||
builder: Snackbar.() -> Unit
|
|
||||||
) = Snackbar.make(view, message, length).setAnchorView(anchor).apply(builder).show()
|
|
||||||
|
|
||||||
override fun invoke(activity: UIActivity<*>) {
|
|
||||||
snackbar(activity.snackbarView, activity.snackbarAnchorView,
|
|
||||||
msg.getText(activity.resources).toString(),
|
|
||||||
length, builder)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -5,12 +5,16 @@ import android.content.Context
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.topjohnwu.magisk.MainDirections
|
import com.topjohnwu.magisk.MainDirections
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.*
|
import com.topjohnwu.magisk.arch.*
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
|
import com.topjohnwu.magisk.utils.TextHolder
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
|
import com.topjohnwu.magisk.utils.asText
|
||||||
import com.topjohnwu.magisk.view.Shortcuts
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
|
|
||||||
class PermissionEvent(
|
class PermissionEvent(
|
||||||
@@ -67,7 +71,7 @@ class NavigationEvent(
|
|||||||
) : ViewEvent(), ActivityExecutor {
|
) : ViewEvent(), ActivityExecutor {
|
||||||
override fun invoke(activity: UIActivity<*>) {
|
override fun invoke(activity: UIActivity<*>) {
|
||||||
(activity as? NavigationActivity<*>)?.apply {
|
(activity as? NavigationActivity<*>)?.apply {
|
||||||
if (pop) navigation?.popBackStack()
|
if (pop) navigation.popBackStack()
|
||||||
directions.navigate()
|
directions.navigate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +87,7 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
|||||||
override fun invoke(fragment: BaseFragment<*>) {
|
override fun invoke(fragment: BaseFragment<*>) {
|
||||||
try {
|
try {
|
||||||
fragment.apply {
|
fragment.apply {
|
||||||
activity.getContent("application/zip") {
|
activity?.getContent("application/zip") {
|
||||||
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
|
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,3 +96,26 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SnackbarEvent(
|
||||||
|
private val msg: TextHolder,
|
||||||
|
private val length: Int = Snackbar.LENGTH_SHORT,
|
||||||
|
private val builder: Snackbar.() -> Unit = {}
|
||||||
|
) : ViewEvent(), ActivityExecutor {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@StringRes res: Int,
|
||||||
|
length: Int = Snackbar.LENGTH_SHORT,
|
||||||
|
builder: Snackbar.() -> Unit = {}
|
||||||
|
) : this(res.asText(), length, builder)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
msg: String,
|
||||||
|
length: Int = Snackbar.LENGTH_SHORT,
|
||||||
|
builder: Snackbar.() -> Unit = {}
|
||||||
|
) : this(msg.asText(), length, builder)
|
||||||
|
|
||||||
|
override fun invoke(activity: UIActivity<*>) {
|
||||||
|
activity.showSnackbar(msg.getText(activity.resources), length, builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -29,7 +29,7 @@ class ManagerInstallDialog : MarkDownDialog() {
|
|||||||
setCancelable(true)
|
setCancelable(true)
|
||||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
text = R.string.install
|
text = R.string.install
|
||||||
onClick { DownloadService.start(context, Subject.Manager()) }
|
onClick { DownloadService.start(context, Subject.App()) }
|
||||||
}
|
}
|
||||||
setButton(MagiskDialog.ButtonType.NEGATIVE) {
|
setButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||||
text = android.R.string.cancel
|
text = android.R.string.cancel
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.events.dialog
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R
|
|
||||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
|
||||||
|
|
||||||
class RestoreAppDialog : DialogEvent() {
|
|
||||||
override fun build(dialog: MagiskDialog) {
|
|
||||||
dialog.apply {
|
|
||||||
setTitle(R.string.settings_restore_app_title)
|
|
||||||
setMessage(R.string.restore_app_confirmation)
|
|
||||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
|
||||||
text = android.R.string.ok
|
|
||||||
onClick { HideAPK.restore(dialog.ownerActivity!!) }
|
|
||||||
}
|
|
||||||
setButton(MagiskDialog.ButtonType.NEGATIVE) {
|
|
||||||
text = android.R.string.cancel
|
|
||||||
}
|
|
||||||
setCancelable(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -34,7 +34,7 @@ class UninstallDialog : DialogEvent() {
|
|||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shell.su("restore_imgs").submit { result ->
|
Shell.cmd("restore_imgs").submit { result ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT)
|
Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT)
|
||||||
|
@@ -84,7 +84,7 @@ fun Intent.startActivity(context: Context) = context.startActivity(this)
|
|||||||
fun Intent.startActivityWithRoot() {
|
fun Intent.startActivityWithRoot() {
|
||||||
val args = mutableListOf("am", "start", "--user", Const.USER_ID.toString())
|
val args = mutableListOf("am", "start", "--user", Const.USER_ID.toString())
|
||||||
val cmd = toCommand(args).joinToString(" ")
|
val cmd = toCommand(args).joinToString(" ")
|
||||||
Shell.su(cmd).submit()
|
Shell.cmd(cmd).submit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Intent.toCommand(args: MutableList<String> = mutableListOf()): MutableList<String> {
|
fun Intent.toCommand(args: MutableList<String> = mutableListOf()): MutableList<String> {
|
||||||
|
@@ -8,14 +8,14 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
fun reboot(reason: String = if (Config.recovery) "recovery" else "") {
|
fun reboot(reason: String = if (Config.recovery) "recovery" else "") {
|
||||||
Shell.su("/system/bin/svc power reboot $reason || /system/bin/reboot $reason").submit()
|
Shell.cmd("/system/bin/svc power reboot $reason || /system/bin/reboot $reason").submit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun relaunchApp(context: Context) {
|
fun relaunchApp(context: Context) {
|
||||||
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) ?: return
|
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) ?: return
|
||||||
val args = mutableListOf("am", "start", "--user", Const.USER_ID.toString())
|
val args = mutableListOf("am", "start", "--user", Const.USER_ID.toString())
|
||||||
val cmd = intent.toCommand(args).joinToString(separator = " ")
|
val cmd = intent.toCommand(args).joinToString(separator = " ")
|
||||||
Shell.su("run_delay 1 \"$cmd\"").exec()
|
Shell.cmd("run_delay 1 \"$cmd\"").exec()
|
||||||
Runtime.getRuntime().exit(0)
|
Runtime.getRuntime().exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -492,7 +492,7 @@ public class SignApk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void sign(X509Certificate cert, PrivateKey key,
|
public static void sign(X509Certificate cert, PrivateKey key,
|
||||||
JarMap inputJar, FileOutputStream outputFile) throws Exception {
|
JarMap inputJar, OutputStream outputStream) throws Exception {
|
||||||
int alignment = 4;
|
int alignment = 4;
|
||||||
int hashes = 0;
|
int hashes = 0;
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ public class SignApk {
|
|||||||
// This assumes outputChunks are array-backed. To avoid this assumption, the
|
// This assumes outputChunks are array-backed. To avoid this assumption, the
|
||||||
// code could be rewritten to use FileChannel.
|
// code could be rewritten to use FileChannel.
|
||||||
for (ByteBuffer outputChunk : outputChunks) {
|
for (ByteBuffer outputChunk : outputChunks) {
|
||||||
outputFile.write(outputChunk.array(),
|
outputStream.write(outputChunk.array(),
|
||||||
outputChunk.arrayOffset() + outputChunk.position(), outputChunk.remaining());
|
outputChunk.arrayOffset() + outputChunk.position(), outputChunk.remaining());
|
||||||
outputChunk.position(outputChunk.limit());
|
outputChunk.position(outputChunk.limit());
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,10 @@ import com.topjohnwu.magisk.MainDirections
|
|||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.BaseMainActivity
|
import com.topjohnwu.magisk.arch.BaseMainActivity
|
||||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||||
import com.topjohnwu.magisk.core.*
|
import com.topjohnwu.magisk.core.Config
|
||||||
|
import com.topjohnwu.magisk.core.Const
|
||||||
|
import com.topjohnwu.magisk.core.Info
|
||||||
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
|
import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
|
||||||
import com.topjohnwu.magisk.di.viewModel
|
import com.topjohnwu.magisk.di.viewModel
|
||||||
import com.topjohnwu.magisk.ktx.startAnimations
|
import com.topjohnwu.magisk.ktx.startAnimations
|
||||||
@@ -51,7 +54,7 @@ class MainActivity : BaseMainActivity<ActivityMainMd2Binding>() {
|
|||||||
|
|
||||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||||
|
|
||||||
navigation?.addOnDestinationChangedListener { _, destination, _ ->
|
navigation.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
isRootFragment = when (destination.id) {
|
isRootFragment = when (destination.id) {
|
||||||
R.id.homeFragment,
|
R.id.homeFragment,
|
||||||
R.id.modulesFragment,
|
R.id.modulesFragment,
|
||||||
|
@@ -26,7 +26,7 @@ class DenyListFragment : BaseFragment<FragmentDenyMd2Binding>() {
|
|||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
activity.setTitle(R.string.denylist)
|
activity?.setTitle(R.string.denylist)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -103,7 +103,7 @@ class ProcessRvItem(
|
|||||||
set(value) = set(value, process.isEnabled, { process.isEnabled = it }, BR.enabled) {
|
set(value) = set(value, process.isEnabled, { process.isEnabled = it }, BR.enabled) {
|
||||||
val arg = if (it) "add" else "rm"
|
val arg = if (it) "add" else "rm"
|
||||||
val (name, pkg) = process
|
val (name, pkg) = process
|
||||||
Shell.su("magisk --denylist $arg $pkg \'$name\'").submit()
|
Shell.cmd("magisk --denylist $arg $pkg \'$name\'").submit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggle() {
|
fun toggle() {
|
||||||
|
@@ -52,7 +52,7 @@ class DenyListViewModel : BaseViewModel() {
|
|||||||
state = State.LOADING
|
state = State.LOADING
|
||||||
val (apps, diff) = withContext(Dispatchers.Default) {
|
val (apps, diff) = withContext(Dispatchers.Default) {
|
||||||
val pm = AppContext.packageManager
|
val pm = AppContext.packageManager
|
||||||
val denyList = Shell.su("magisk --denylist ls").exec().out
|
val denyList = Shell.cmd("magisk --denylist ls").exec().out
|
||||||
.map { CmdlineListItem(it) }
|
.map { CmdlineListItem(it) }
|
||||||
val apps = pm.getInstalledApplications(MATCH_UNINSTALLED_PACKAGES).run {
|
val apps = pm.getInstalledApplications(MATCH_UNINSTALLED_PACKAGES).run {
|
||||||
asFlow()
|
asFlow()
|
||||||
|
@@ -31,10 +31,10 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
activity.setTitle(R.string.flash_screen_title)
|
activity?.setTitle(R.string.flash_screen_title)
|
||||||
|
|
||||||
viewModel.subtitle.observe(this) {
|
viewModel.subtitle.observe(this) {
|
||||||
activity.supportActionBar?.setSubtitle(it)
|
activity?.supportActionBar?.setSubtitle(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,15 +49,15 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
defaultOrientation = activity.requestedOrientation
|
defaultOrientation = activity?.requestedOrientation ?: -1
|
||||||
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||||
viewModel.startFlashing()
|
viewModel.startFlashing()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("WrongConstant")
|
@SuppressLint("WrongConstant")
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
if (defaultOrientation != -1) {
|
if (defaultOrientation != -1) {
|
||||||
activity.requestedOrientation = defaultOrientation
|
activity?.requestedOrientation = defaultOrientation
|
||||||
}
|
}
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
@@ -103,9 +103,11 @@ class FlashViewModel : BaseViewModel() {
|
|||||||
val name = "magisk_install_log_%s.log".format(now.toTime(timeFormatStandard))
|
val name = "magisk_install_log_%s.log".format(now.toTime(timeFormatStandard))
|
||||||
val file = MediaStoreUtils.getFile(name, true)
|
val file = MediaStoreUtils.getFile(name, true)
|
||||||
file.uri.outputStream().bufferedWriter().use { writer ->
|
file.uri.outputStream().bufferedWriter().use { writer ->
|
||||||
logItems.forEach {
|
synchronized(logItems) {
|
||||||
writer.write(it)
|
logItems.forEach {
|
||||||
writer.newLine()
|
writer.write(it)
|
||||||
|
writer.newLine()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SnackbarEvent(file.toString()).publish()
|
SnackbarEvent(file.toString()).publish()
|
||||||
|
@@ -19,7 +19,7 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>() {
|
|||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
activity.title = resources.getString(R.string.section_home)
|
activity?.title = resources.getString(R.string.section_home)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
DownloadService.observeProgress(this, viewModel::onProgressUpdate)
|
DownloadService.observeProgress(this, viewModel::onProgressUpdate)
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>() {
|
|||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_settings ->
|
R.id.action_settings ->
|
||||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
|
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
|
||||||
R.id.action_reboot -> RebootEvent.inflateMenu(activity).show()
|
R.id.action_reboot -> activity?.let { RebootEvent.inflateMenu(it).show() }
|
||||||
else -> return super.onOptionsItemSelected(item)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@@ -10,7 +10,7 @@ import com.topjohnwu.magisk.arch.*
|
|||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
import com.topjohnwu.magisk.core.download.Subject
|
||||||
import com.topjohnwu.magisk.core.download.Subject.Manager
|
import com.topjohnwu.magisk.core.download.Subject.App
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||||
import com.topjohnwu.magisk.databinding.itemBindingOf
|
import com.topjohnwu.magisk.databinding.itemBindingOf
|
||||||
import com.topjohnwu.magisk.databinding.set
|
import com.topjohnwu.magisk.databinding.set
|
||||||
@@ -112,7 +112,7 @@ class HomeViewModel(
|
|||||||
}.publish()
|
}.publish()
|
||||||
|
|
||||||
fun onProgressUpdate(progress: Float, subject: Subject) {
|
fun onProgressUpdate(progress: Float, subject: Subject) {
|
||||||
if (subject is Manager)
|
if (subject is App)
|
||||||
stateManagerProgress = progress.times(100f).roundToInt()
|
stateManagerProgress = progress.times(100f).roundToInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +123,11 @@ class HomeViewModel(
|
|||||||
fun onDeletePressed() = UninstallDialog().publish()
|
fun onDeletePressed() = UninstallDialog().publish()
|
||||||
|
|
||||||
fun onManagerPressed() = when (state) {
|
fun onManagerPressed() = when (state) {
|
||||||
State.LOADED -> withExternalRW { ManagerInstallDialog().publish() }
|
State.LOADED -> withExternalRW {
|
||||||
|
withInstallPermission {
|
||||||
|
ManagerInstallDialog().publish()
|
||||||
|
}
|
||||||
|
}
|
||||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||||
else -> SnackbarEvent(R.string.no_connection).publish()
|
else -> SnackbarEvent(R.string.no_connection).publish()
|
||||||
}
|
}
|
||||||
@@ -140,7 +144,7 @@ class HomeViewModel(
|
|||||||
private suspend fun ensureEnv() {
|
private suspend fun ensureEnv() {
|
||||||
if (MagiskState.NOT_INSTALLED == stateMagisk || checkedEnv) return
|
if (MagiskState.NOT_INSTALLED == stateMagisk || checkedEnv) return
|
||||||
val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}"
|
val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}"
|
||||||
if (!Shell.su(cmd).await().isSuccess) {
|
if (!Shell.cmd(cmd).await().isSuccess) {
|
||||||
EnvFixDialog(this).publish()
|
EnvFixDialog(this).publish()
|
||||||
}
|
}
|
||||||
checkedEnv = true
|
checkedEnv = true
|
||||||
|
@@ -38,7 +38,7 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
activity.title = resources.getString(R.string.logs)
|
activity?.title = resources.getString(R.string.logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@@ -19,7 +19,7 @@ class ModuleFragment : BaseFragment<FragmentModuleMd2Binding>() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
activity.title = resources.getString(R.string.modules)
|
activity?.title = resources.getString(R.string.modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@@ -18,7 +18,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsMd2Binding>() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
|
||||||
activity.title = resources.getString(R.string.settings)
|
activity?.title = resources.getString(R.string.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@@ -25,6 +25,7 @@ import com.topjohnwu.magisk.databinding.set
|
|||||||
import com.topjohnwu.magisk.di.AppContext
|
import com.topjohnwu.magisk.di.AppContext
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.utils.asText
|
import com.topjohnwu.magisk.utils.asText
|
||||||
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -105,6 +106,26 @@ object Hide : BaseSettingsItem.Input() {
|
|||||||
object Restore : BaseSettingsItem.Blank() {
|
object Restore : BaseSettingsItem.Blank() {
|
||||||
override val title = R.string.settings_restore_app_title.asText()
|
override val title = R.string.settings_restore_app_title.asText()
|
||||||
override val description = R.string.settings_restore_app_summary.asText()
|
override val description = R.string.settings_restore_app_summary.asText()
|
||||||
|
|
||||||
|
override fun onPressed(view: View, handler: Handler) {
|
||||||
|
handler.onItemPressed(view, this) {
|
||||||
|
MagiskDialog(view.context).apply {
|
||||||
|
setTitle(R.string.settings_restore_app_title)
|
||||||
|
setMessage(R.string.restore_app_confirmation)
|
||||||
|
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
|
text = android.R.string.ok
|
||||||
|
onClick {
|
||||||
|
handler.onItemAction(view, this@Restore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||||
|
text = android.R.string.cancel
|
||||||
|
}
|
||||||
|
setCancelable(true)
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AddShortcut : BaseSettingsItem.Blank() {
|
object AddShortcut : BaseSettingsItem.Blank() {
|
||||||
@@ -236,7 +257,7 @@ object DenyList : BaseSettingsItem.Toggle() {
|
|||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
val cmd = if (value) "enable" else "disable"
|
val cmd = if (value) "enable" else "disable"
|
||||||
Shell.su("magisk --denylist $cmd").submit { result ->
|
Shell.cmd("magisk --denylist $cmd").submit { result ->
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
Config.denyList = value
|
Config.denyList = value
|
||||||
} else {
|
} else {
|
||||||
|
@@ -20,7 +20,6 @@ import com.topjohnwu.magisk.events.AddHomeIconEvent
|
|||||||
import com.topjohnwu.magisk.events.RecreateEvent
|
import com.topjohnwu.magisk.events.RecreateEvent
|
||||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||||
import com.topjohnwu.magisk.events.dialog.RestoreAppDialog
|
|
||||||
import com.topjohnwu.magisk.ktx.activity
|
import com.topjohnwu.magisk.ktx.activity
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
@@ -103,7 +102,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate()
|
Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate()
|
||||||
DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate()
|
DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate()
|
||||||
SystemlessHosts -> createHosts()
|
SystemlessHosts -> createHosts()
|
||||||
Restore -> RestoreAppDialog().publish()
|
Hide, Restore -> withInstallPermission(andThen)
|
||||||
AddShortcut -> AddHomeIconEvent().publish()
|
AddShortcut -> AddHomeIconEvent().publish()
|
||||||
else -> andThen()
|
else -> andThen()
|
||||||
}
|
}
|
||||||
@@ -114,6 +113,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
Language -> RecreateEvent().publish()
|
Language -> RecreateEvent().publish()
|
||||||
UpdateChannel -> openUrlIfNecessary(view)
|
UpdateChannel -> openUrlIfNecessary(view)
|
||||||
is Hide -> viewModelScope.launch { HideAPK.hide(view.activity, item.value) }
|
is Hide -> viewModelScope.launch { HideAPK.hide(view.activity, item.value) }
|
||||||
|
Restore -> viewModelScope.launch { HideAPK.restore(view.activity) }
|
||||||
Zygisk -> if (Zygisk.mismatch) SnackbarEvent(R.string.reboot_apply_change).publish()
|
Zygisk -> if (Zygisk.mismatch) SnackbarEvent(R.string.reboot_apply_change).publish()
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createHosts() {
|
private fun createHosts() {
|
||||||
Shell.su("add_hosts_module").submit {
|
Shell.cmd("add_hosts_module").submit {
|
||||||
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
|
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ class SuperuserFragment : BaseFragment<FragmentSuperuserMd2Binding>() {
|
|||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
activity.title = resources.getString(R.string.superuser)
|
activity?.title = resources.getString(R.string.superuser)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@@ -73,7 +73,7 @@ class SuRequestViewModel(
|
|||||||
val itemBinding = ItemBinding.of<String>(BR.item, R.layout.item_spinner)
|
val itemBinding = ItemBinding.of<String>(BR.item, R.layout.item_spinner)
|
||||||
|
|
||||||
private val handler = SuRequestHandler(AppContext.packageManager, policyDB)
|
private val handler = SuRequestHandler(AppContext.packageManager, policyDB)
|
||||||
private lateinit var timer: CountDownTimer
|
private var timer: CountDownTimer? = null
|
||||||
|
|
||||||
fun grantPressed() {
|
fun grantPressed() {
|
||||||
cancelTimer()
|
cancelTimer()
|
||||||
@@ -121,7 +121,7 @@ class SuRequestViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun respond(action: Int) {
|
private fun respond(action: Int) {
|
||||||
timer.cancel()
|
timer?.cancel()
|
||||||
|
|
||||||
val pos = selectedItemPosition
|
val pos = selectedItemPosition
|
||||||
timeoutPrefs.edit().putInt(handler.policy.packageName, pos).apply()
|
timeoutPrefs.edit().putInt(handler.policy.packageName, pos).apply()
|
||||||
@@ -132,7 +132,7 @@ class SuRequestViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelTimer() {
|
private fun cancelTimer() {
|
||||||
timer.cancel()
|
timer?.cancel()
|
||||||
denyText.seconds = 0
|
denyText.seconds = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -61,7 +61,7 @@ class ThemeFragment : BaseFragment<FragmentThemeMd2Binding>() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
|
||||||
activity.title = getString(R.string.section_theme)
|
activity?.title = getString(R.string.section_theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -121,15 +121,15 @@ class MagiskDialog(
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
super.setContentView(binding.root)
|
super.setContentView(binding.root)
|
||||||
|
|
||||||
val default = MaterialColors.getColor(context, R.attr.colorSurface, javaClass.canonicalName)
|
val default = MaterialColors.getColor(context, com.google.android.material.R.attr.colorSurface, javaClass.canonicalName)
|
||||||
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, default)
|
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, default)
|
||||||
val materialShapeDrawable = MaterialShapeDrawable(context, null, R.attr.alertDialogStyle, R.style.MaterialAlertDialog_MaterialComponents)
|
val materialShapeDrawable = MaterialShapeDrawable(context, null, androidx.appcompat.R.attr.alertDialogStyle, com.google.android.material.R.style.MaterialAlertDialog_MaterialComponents)
|
||||||
materialShapeDrawable.initializeElevationOverlay(context)
|
materialShapeDrawable.initializeElevationOverlay(context)
|
||||||
materialShapeDrawable.fillColor = ColorStateList.valueOf(surfaceColor)
|
materialShapeDrawable.fillColor = ColorStateList.valueOf(surfaceColor)
|
||||||
materialShapeDrawable.elevation = context.resources.getDimension(R.dimen.margin_generic)
|
materialShapeDrawable.elevation = context.resources.getDimension(R.dimen.margin_generic)
|
||||||
materialShapeDrawable.setCornerSize(context.resources.getDimension(R.dimen.l_50))
|
materialShapeDrawable.setCornerSize(context.resources.getDimension(R.dimen.l_50))
|
||||||
|
|
||||||
val inset = context.resources.getDimensionPixelSize(R.dimen.appcompat_dialog_background_inset)
|
val inset = context.resources.getDimensionPixelSize(com.google.android.material.R.dimen.appcompat_dialog_background_inset)
|
||||||
window?.apply {
|
window?.apply {
|
||||||
setBackgroundDrawable(InsetDrawable(materialShapeDrawable, inset, inset, inset, inset))
|
setBackgroundDrawable(InsetDrawable(materialShapeDrawable, inset, inset, inset, inset))
|
||||||
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
@@ -1,15 +1,16 @@
|
|||||||
package com.topjohnwu.magisk.view
|
package com.topjohnwu.magisk.view
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.graphics.drawable.toIcon
|
import androidx.core.graphics.drawable.toIcon
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Const.ID.PROGRESS_NOTIFICATION_CHANNEL
|
|
||||||
import com.topjohnwu.magisk.core.Const.ID.UPDATE_NOTIFICATION_CHANNEL
|
|
||||||
import com.topjohnwu.magisk.core.download.DownloadService
|
import com.topjohnwu.magisk.core.download.DownloadService
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
import com.topjohnwu.magisk.core.download.Subject
|
||||||
import com.topjohnwu.magisk.di.AppContext
|
import com.topjohnwu.magisk.di.AppContext
|
||||||
@@ -21,51 +22,80 @@ object Notifications {
|
|||||||
|
|
||||||
val mgr by lazy { AppContext.getSystemService<NotificationManager>()!! }
|
val mgr by lazy { AppContext.getSystemService<NotificationManager>()!! }
|
||||||
|
|
||||||
private const val APK_UPDATE_NOTIFICATION_ID = 5
|
private const val APP_UPDATED_NOTIFICATION_ID = 4
|
||||||
private val nextId = AtomicInteger(APK_UPDATE_NOTIFICATION_ID)
|
private const val APP_UPDATE_NOTIFICATION_ID = 5
|
||||||
|
|
||||||
|
private const val UPDATE_CHANNEL = "update"
|
||||||
|
private const val PROGRESS_CHANNEL = "progress"
|
||||||
|
private const val UPDATED_CHANNEL = "updated"
|
||||||
|
|
||||||
|
private val nextId = AtomicInteger(APP_UPDATE_NOTIFICATION_ID)
|
||||||
|
|
||||||
fun setup(context: Context) {
|
fun setup(context: Context) {
|
||||||
if (SDK_INT >= 26) {
|
if (SDK_INT >= 26) {
|
||||||
val channel = NotificationChannel(UPDATE_NOTIFICATION_CHANNEL,
|
val channel = NotificationChannel(UPDATE_CHANNEL,
|
||||||
context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT)
|
context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT)
|
||||||
val channel2 = NotificationChannel(PROGRESS_NOTIFICATION_CHANNEL,
|
val channel2 = NotificationChannel(PROGRESS_CHANNEL,
|
||||||
context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW)
|
context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW)
|
||||||
mgr.createNotificationChannels(listOf(channel, channel2))
|
val channel3 = NotificationChannel(UPDATED_CHANNEL,
|
||||||
|
context.getString(R.string.updated_channel), NotificationManager.IMPORTANCE_HIGH)
|
||||||
|
mgr.createNotificationChannels(listOf(channel, channel2, channel3))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateBuilder(context: Context): Notification.Builder {
|
fun selfLaunchIntent(context: Context): Intent {
|
||||||
return Notification.Builder(context).apply {
|
val pm = context.packageManager
|
||||||
val bitmap = context.getBitmap(R.drawable.ic_magisk_outline)
|
val intent = pm.getLaunchIntentForPackage(context.packageName)!!
|
||||||
setLargeIcon(bitmap)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
if (SDK_INT >= 26) {
|
return intent
|
||||||
setSmallIcon(bitmap.toIcon())
|
|
||||||
setChannelId(UPDATE_NOTIFICATION_CHANNEL)
|
|
||||||
} else {
|
|
||||||
setSmallIcon(R.drawable.ic_magisk_outline)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun managerUpdate(context: Context) {
|
@SuppressLint("InlinedApi")
|
||||||
val intent = DownloadService.getPendingIntent(context, Subject.Manager())
|
fun updateDone(context: Context) {
|
||||||
|
setup(context)
|
||||||
|
val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
val pending = PendingIntent.getActivity(context, 0, selfLaunchIntent(context), flag)
|
||||||
|
val builder = if (SDK_INT >= 26) {
|
||||||
|
Notification.Builder(context, UPDATED_CHANNEL)
|
||||||
|
.setSmallIcon(context.getBitmap(R.drawable.ic_magisk_outline).toIcon())
|
||||||
|
} else {
|
||||||
|
Notification.Builder(context).setPriority(Notification.PRIORITY_HIGH)
|
||||||
|
.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
|
}
|
||||||
|
.setContentIntent(pending)
|
||||||
|
.setContentTitle(context.getText(R.string.updated_title))
|
||||||
|
.setContentText(context.getText(R.string.updated_text))
|
||||||
|
.setAutoCancel(true)
|
||||||
|
mgr.notify(APP_UPDATED_NOTIFICATION_ID, builder.build())
|
||||||
|
}
|
||||||
|
|
||||||
val builder = updateBuilder(context)
|
fun updateAvailable(context: Context) {
|
||||||
|
val intent = DownloadService.getPendingIntent(context, Subject.App())
|
||||||
|
|
||||||
|
val bitmap = context.getBitmap(R.drawable.ic_magisk_outline)
|
||||||
|
val builder = if (SDK_INT >= 26) {
|
||||||
|
Notification.Builder(context, UPDATE_CHANNEL)
|
||||||
|
.setSmallIcon(bitmap.toIcon())
|
||||||
|
} else {
|
||||||
|
Notification.Builder(context)
|
||||||
|
.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
|
}
|
||||||
|
.setLargeIcon(bitmap)
|
||||||
.setContentTitle(context.getString(R.string.magisk_update_title))
|
.setContentTitle(context.getString(R.string.magisk_update_title))
|
||||||
.setContentText(context.getString(R.string.manager_download_install))
|
.setContentText(context.getString(R.string.manager_download_install))
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setContentIntent(intent)
|
.setContentIntent(intent)
|
||||||
|
|
||||||
mgr.notify(APK_UPDATE_NOTIFICATION_ID, builder.build())
|
mgr.notify(APP_UPDATE_NOTIFICATION_ID, builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun progress(context: Context, title: CharSequence): Notification.Builder {
|
fun progress(context: Context, title: CharSequence): Notification.Builder {
|
||||||
val builder = if (SDK_INT >= 26) {
|
val builder = if (SDK_INT >= 26) {
|
||||||
Notification.Builder(context, PROGRESS_NOTIFICATION_CHANNEL)
|
Notification.Builder(context, PROGRESS_CHANNEL)
|
||||||
} else {
|
} else {
|
||||||
Notification.Builder(context).setPriority(Notification.PRIORITY_LOW)
|
Notification.Builder(context).setPriority(Notification.PRIORITY_LOW)
|
||||||
}
|
}
|
||||||
builder.setSmallIcon(android.R.drawable.stat_sys_download)
|
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setProgress(0, 0, true)
|
.setProgress(0, 0, true)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
|
@@ -29,11 +29,11 @@ public class ConcealableBottomNavigationView extends BottomNavigationView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||||
this(context, attrs, R.attr.bottomNavigationStyle);
|
this(context, attrs, com.google.android.material.R.attr.bottomNavigationStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||||
this(context, attrs, defStyleAttr, R.style.Widget_Design_BottomNavigationView);
|
this(context, attrs, defStyleAttr, com.google.android.material.R.style.Widget_Design_BottomNavigationView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public ConcealableBottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
@@ -8,16 +8,19 @@
|
|||||||
<string name="install">Installi</string>
|
<string name="install">Installi</string>
|
||||||
<string name="section_home">Kodu</string>
|
<string name="section_home">Kodu</string>
|
||||||
<string name="section_theme">Teemad</string>
|
<string name="section_theme">Teemad</string>
|
||||||
|
<string name="denylist">Keeluloend</string>
|
||||||
|
|
||||||
<!--Home-->
|
<!--Home-->
|
||||||
<string name="no_connection">Ühendus puudub</string>
|
<string name="no_connection">Ühendus puudub</string>
|
||||||
<string name="app_changelog">Muudatuste logi</string>
|
<string name="app_changelog">Muudatuste logi</string>
|
||||||
<string name="loading">Laadimine…</string>
|
<string name="loading">Laadimine…</string>
|
||||||
<string name="update">Uuenda</string>
|
<string name="update">Uuenda</string>
|
||||||
<string name="not_available">puudub</string>
|
<string name="not_available">teadmata</string>
|
||||||
<string name="hide">Peida</string>
|
<string name="hide">Peida</string>
|
||||||
<string name="home_package">Pakett</string>
|
<string name="home_package">Pakett</string>
|
||||||
|
<string name="home_app_title">Rakendus</string>
|
||||||
|
|
||||||
|
<string name="home_notice_content">Laadi Magisk alla vaid ametlikult GitHubi lehelt. Tundmatutest allikatest laaditud failid võivad olla pahatahtlikud!</string>
|
||||||
<string name="home_support_title">Toeta meid</string>
|
<string name="home_support_title">Toeta meid</string>
|
||||||
<string name="home_item_source">Lähtekood</string>
|
<string name="home_item_source">Lähtekood</string>
|
||||||
<string name="home_support_content">Magisk on ja jääb alati tasuta ning avatud lähtekoodiga kättesaadavaks. Siiski, sa võid meile väikese annetuse näol toetust üles näidata.</string>
|
<string name="home_support_content">Magisk on ja jääb alati tasuta ning avatud lähtekoodiga kättesaadavaks. Siiski, sa võid meile väikese annetuse näol toetust üles näidata.</string>
|
||||||
@@ -25,20 +28,21 @@
|
|||||||
<string name="home_latest_version">Viimatine</string>
|
<string name="home_latest_version">Viimatine</string>
|
||||||
<string name="invalid_update_channel">Sobimatu uuenduste kanal</string>
|
<string name="invalid_update_channel">Sobimatu uuenduste kanal</string>
|
||||||
<string name="uninstall_magisk_title">Eemalda Magisk</string>
|
<string name="uninstall_magisk_title">Eemalda Magisk</string>
|
||||||
<string name="uninstall_magisk_msg">Kõik moodulid keelatakse/eemaldatakse!\nJuurkasutaja eemaldatakse!\nSinu andmeid võidakse krüpteerida, kui need juba ei ole krüpteeritud!</string>
|
<string name="uninstall_magisk_msg">Kõik moodulid keelatakse/eemaldatakse!\nJuurkasutaja eemaldatakse!\nMistahes sisemälu failid, millelt eemaldati Magiski tarbeks krüpteering, krüpteeritakse taas!</string>
|
||||||
|
|
||||||
<!--Install-->
|
<!--Install-->
|
||||||
<string name="keep_force_encryption">Säilita sunnitud krüpteering</string>
|
<string name="keep_force_encryption">Säilita sunnitud krüpteering</string>
|
||||||
<string name="keep_dm_verity">Säilita AVB 2.0/dm-verity</string>
|
<string name="keep_dm_verity">Säilita AVB 2.0/dm-verity</string>
|
||||||
|
<string name="patch_vbmeta">Paika käivitustõmmises vbmeta</string>
|
||||||
<string name="recovery_mode">Taastusrežiim</string>
|
<string name="recovery_mode">Taastusrežiim</string>
|
||||||
<string name="install_options_title">Valikud</string>
|
<string name="install_options_title">Valikud</string>
|
||||||
<string name="install_method_title">Meetod</string>
|
<string name="install_method_title">Meetod</string>
|
||||||
<string name="install_next">Edasi</string>
|
<string name="install_next">Edasi</string>
|
||||||
<string name="install_start">Läksime</string>
|
<string name="install_start">Läksime</string>
|
||||||
<string name="manager_download_install">Vajuta allalaadimiseks ja installimiseks.</string>
|
<string name="manager_download_install">Vajuta allalaadimiseks ja installimiseks</string>
|
||||||
<string name="direct_install">Otsene install (soovitatud)</string>
|
<string name="direct_install">Otsene install (soovitatud)</string>
|
||||||
<string name="install_inactive_slot">Installi ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust)</string>
|
<string name="install_inactive_slot">Installi ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust)</string>
|
||||||
<string name="install_inactive_slot_msg">Sinu seade SUNNITAKSE peale taaskäivitust käivituma praegusesse ebaaktiivsesse lahtrisse!\nKasuta seda valikut vaid peale üle-õhu uuenduse teostamist.\nJätkad?</string>
|
<string name="install_inactive_slot_msg">Pärast taaskäivitust SUNNITAKSE sinu seade käivituma praegusesse ebaaktiivsesse lahtrisse!\nKasuta seda valikut vaid peale üle-õhu uuenduse teostamist.\nJätkad?</string>
|
||||||
<string name="setup_title">Lisaseadistus</string>
|
<string name="setup_title">Lisaseadistus</string>
|
||||||
<string name="select_patch_file">Vali ja paika fail</string>
|
<string name="select_patch_file">Vali ja paika fail</string>
|
||||||
<string name="patch_file_msg">Vali toortõmmis (*.img) või ODIN tar-fail (*.tar)</string>
|
<string name="patch_file_msg">Vali toortõmmis (*.img) või ODIN tar-fail (*.tar)</string>
|
||||||
@@ -47,6 +51,7 @@
|
|||||||
|
|
||||||
<!--Superuser-->
|
<!--Superuser-->
|
||||||
<string name="su_request_title">Superkasutaja taotlus</string>
|
<string name="su_request_title">Superkasutaja taotlus</string>
|
||||||
|
<string name="touch_filtered_warning">Kuna rakendus varjab superkasutaja päringut, ei saa Magisk sinu vastust kinnitada</string>
|
||||||
<string name="deny">Keela</string>
|
<string name="deny">Keela</string>
|
||||||
<string name="prompt">Küsi</string>
|
<string name="prompt">Küsi</string>
|
||||||
<string name="grant">Luba</string>
|
<string name="grant">Luba</string>
|
||||||
@@ -75,8 +80,8 @@
|
|||||||
<string name="superuser_policy_none">Ükski rakendus ei ole veel superkasutaja õigusi küsinud.</string>
|
<string name="superuser_policy_none">Ükski rakendus ei ole veel superkasutaja õigusi küsinud.</string>
|
||||||
|
|
||||||
<!--Logs-->
|
<!--Logs-->
|
||||||
<string name="log_data_none">Sa oled logivaba, proovi oma superkasutaja õigustega rakendusi rohkem kasutada.</string>
|
<string name="log_data_none">Sa oled logivaba, proovi oma superkasutaja õigustega rakendusi rohkem kasutada</string>
|
||||||
<string name="log_data_magisk_none">Magiski logid on tühjad, see on imelik.</string>
|
<string name="log_data_magisk_none">Magiski logid on tühjad, see on imelik</string>
|
||||||
<string name="menuSaveLog">Salvesta logi</string>
|
<string name="menuSaveLog">Salvesta logi</string>
|
||||||
<string name="menuClearLog">Tühjenda logi nüüd</string>
|
<string name="menuClearLog">Tühjenda logi nüüd</string>
|
||||||
<string name="logs_cleared">Logi edukalt tühjendatud.</string>
|
<string name="logs_cleared">Logi edukalt tühjendatud.</string>
|
||||||
@@ -85,13 +90,15 @@
|
|||||||
|
|
||||||
<!--SafetyNet-->
|
<!--SafetyNet-->
|
||||||
|
|
||||||
<!-- MagiskHide -->
|
<!--MagiskHide-->
|
||||||
<string name="show_system_app">Kuva süsteemirakendused</string>
|
<string name="show_system_app">Kuva uuendatavad süsteemirakendused</string>
|
||||||
|
<string name="show_os_app">Kuva fikseeritud süsteemirakendused</string>
|
||||||
<string name="hide_filter_hint">Filtreeri nime järgi</string>
|
<string name="hide_filter_hint">Filtreeri nime järgi</string>
|
||||||
<string name="hide_search">Otsing</string>
|
<string name="hide_search">Otsing</string>
|
||||||
|
|
||||||
<!--Module -->
|
<!--Module-->
|
||||||
<string name="no_info_provided">(Info puudub)</string>
|
<string name="no_info_provided">(Info puudub)</string>
|
||||||
|
<string name="reboot_userspace">Pehme taaskäivitus</string>
|
||||||
<string name="reboot_recovery">Taaskäivita taastusesse</string>
|
<string name="reboot_recovery">Taaskäivita taastusesse</string>
|
||||||
<string name="reboot_bootloader">Taaskäivita käivitushaldurisse</string>
|
<string name="reboot_bootloader">Taaskäivita käivitushaldurisse</string>
|
||||||
<string name="reboot_download">Taaskäivita allalaadimisrežiimi</string>
|
<string name="reboot_download">Taaskäivita allalaadimisrežiimi</string>
|
||||||
@@ -101,8 +108,11 @@
|
|||||||
<string name="module_state_restore">Taasta</string>
|
<string name="module_state_restore">Taasta</string>
|
||||||
<string name="module_action_install_external">Installi sisemälust</string>
|
<string name="module_action_install_external">Installi sisemälust</string>
|
||||||
<string name="update_available">Uuendus saadaval</string>
|
<string name="update_available">Uuendus saadaval</string>
|
||||||
|
<string name="suspend_text_riru">Moodul keelatud %1$s lubamise tõttu</string>
|
||||||
|
<string name="suspend_text_zygisk">Moodul keelatud %1$s mittelubamise tõttu</string>
|
||||||
|
<string name="zygisk_module_unloaded">Zygisk-moodulit ei laaditud ühildumatuse tõttu</string>
|
||||||
|
|
||||||
<!--Settings -->
|
<!--Settings-->
|
||||||
<string name="settings_dark_mode_title">Teema režiim</string>
|
<string name="settings_dark_mode_title">Teema režiim</string>
|
||||||
<string name="settings_dark_mode_message">Vali režiim, mis ühtib sinu stiiliga!</string>
|
<string name="settings_dark_mode_message">Vali režiim, mis ühtib sinu stiiliga!</string>
|
||||||
<string name="settings_dark_mode_light">Alati hele</string>
|
<string name="settings_dark_mode_light">Alati hele</string>
|
||||||
@@ -110,20 +120,30 @@
|
|||||||
<string name="settings_dark_mode_dark">Alati tume</string>
|
<string name="settings_dark_mode_dark">Alati tume</string>
|
||||||
<string name="settings_download_path_title">Allalaadimise failitee</string>
|
<string name="settings_download_path_title">Allalaadimise failitee</string>
|
||||||
<string name="settings_download_path_message">Failid salvestatakse kausta %1$s</string>
|
<string name="settings_download_path_message">Failid salvestatakse kausta %1$s</string>
|
||||||
|
<string name="settings_hide_app_title">Peida Magiski rakendus</string>
|
||||||
|
<string name="settings_hide_app_summary">Paigalda juhusliku paketi-ID ja kohandatud nimega puhverrakendus</string>
|
||||||
|
<string name="settings_restore_app_title">Taasta Magiski rakendus</string>
|
||||||
|
<string name="settings_restore_app_summary">Too rakendus peidust välja, taastades originaalse APK</string>
|
||||||
<string name="language">Keel</string>
|
<string name="language">Keel</string>
|
||||||
<string name="system_default">(Süsteemi vaikesäte)</string>
|
<string name="system_default">(Süsteemi vaikesäte)</string>
|
||||||
<string name="settings_check_update_title">Kontrolli uuendusi</string>
|
<string name="settings_check_update_title">Kontrolli uuendusi</string>
|
||||||
<string name="settings_check_update_summary">Kontrolli taustal perioodiliselt uuendusi.</string>
|
<string name="settings_check_update_summary">Kontrolli taustal perioodiliselt uuendusi</string>
|
||||||
<string name="settings_update_channel_title">Uuenduste kanal</string>
|
<string name="settings_update_channel_title">Uuenduste kanal</string>
|
||||||
<string name="settings_update_stable">Stabiilne</string>
|
<string name="settings_update_stable">Stabiilne</string>
|
||||||
<string name="settings_update_beta">Beeta</string>
|
<string name="settings_update_beta">Beeta</string>
|
||||||
<string name="settings_update_custom">Kohandatud</string>
|
<string name="settings_update_custom">Kohandatud</string>
|
||||||
<string name="settings_update_custom_msg">Sisesta kohandatud URL</string>
|
<string name="settings_update_custom_msg">Sisesta kohandatud kanali URL</string>
|
||||||
|
<string name="settings_zygisk_summary">Käivita teatud Magiski osi zygote-protsessis</string>
|
||||||
|
<string name="settings_denylist_title">Jõusta keeluloend</string>
|
||||||
|
<string name="settings_denylist_summary">Keeluloendis olevatel protsessidel tühistatakse kõik Magiski muudatused</string>
|
||||||
|
<string name="settings_denylist_error">See funktsioon eeldab %1$s lubamist</string>
|
||||||
|
<string name="settings_denylist_config_title">Seadista keeluloendit</string>
|
||||||
|
<string name="settings_denylist_config_summary">Vali keeluloendisse lisatavad protsessid</string>
|
||||||
<string name="settings_hosts_title">Süsteemivaba hosts</string>
|
<string name="settings_hosts_title">Süsteemivaba hosts</string>
|
||||||
<string name="settings_hosts_summary">Süsteemivaba hosts-tugi reklaamiblokeerijatest rakendustele</string>
|
<string name="settings_hosts_summary">Süsteemivaba hosts-tugi reklaamiblokeerijatest rakendustele</string>
|
||||||
<string name="settings_hosts_toast">Süsteemivaba hostsi moodul lisatud</string>
|
<string name="settings_hosts_toast">Süsteemivaba hostsi moodul lisatud</string>
|
||||||
<string name="settings_app_name_hint">Uus nimi</string>
|
<string name="settings_app_name_hint">Uus nimi</string>
|
||||||
<string name="settings_app_name_helper">Rakendus pakitakse selle nimega</string>
|
<string name="settings_app_name_helper">Rakendus taaspakitakse selle nimega</string>
|
||||||
<string name="settings_app_name_error">Sobimatu vorming</string>
|
<string name="settings_app_name_error">Sobimatu vorming</string>
|
||||||
<string name="settings_su_app_adb">Rakendused ja ADB</string>
|
<string name="settings_su_app_adb">Rakendused ja ADB</string>
|
||||||
<string name="settings_su_app">Ainult rakendused</string>
|
<string name="settings_su_app">Ainult rakendused</string>
|
||||||
@@ -140,12 +160,14 @@
|
|||||||
<string name="request_timeout">Taotluse ajalõpp</string>
|
<string name="request_timeout">Taotluse ajalõpp</string>
|
||||||
<string name="superuser_notification">Superkasutaja teavitus</string>
|
<string name="superuser_notification">Superkasutaja teavitus</string>
|
||||||
<string name="settings_su_reauth_title">Taas-autendi peale uuendust</string>
|
<string name="settings_su_reauth_title">Taas-autendi peale uuendust</string>
|
||||||
<string name="settings_su_reauth_summary">Taas-autendi superkasutaja õigused peale rakenduse uuendamist</string>
|
<string name="settings_su_reauth_summary">Pärast rakenduste uuendamist küsi superkasutaja luba uuesti</string>
|
||||||
|
<string name="settings_su_tapjack_title">Nupu varjamise kaitse</string>
|
||||||
|
<string name="settings_su_tapjack_summary">Superkasutaja taotluse hüpik ei reageeri vajutusele, kui seda katab mõni teine aken või ülekate</string>
|
||||||
<string name="settings_su_biometric_title">Luba biomeetriaga autentimine</string>
|
<string name="settings_su_biometric_title">Luba biomeetriaga autentimine</string>
|
||||||
<string name="settings_su_biometric_summary">Kasuta superkasutaja taotluste kinnitamiseks biomeetrilist autentimist</string>
|
<string name="settings_su_biometric_summary">Kasuta superkasutaja taotluste kinnitamiseks biomeetrilist autentimist</string>
|
||||||
<string name="no_biometric">Mittetoetatud seade või ükski biomeetriaseadistus pole lubatud</string>
|
<string name="no_biometric">Mittetoetatud seade või ükski biomeetriaseadistus pole lubatud</string>
|
||||||
<string name="settings_customization">Kohandamine</string>
|
<string name="settings_customization">Kohandamine</string>
|
||||||
<string name="setting_add_shortcut_summary">Lisa avakuvale ilus otsetee, juhul kui nime ja ikooni on pärast rakenduse peitmist raske tuvastada</string>
|
<string name="setting_add_shortcut_summary">Lisa avakuvale ilus otsetee juhuks, kui nime ja ikooni on pärast rakenduse peitmist raske tuvastada</string>
|
||||||
<string name="settings_doh_title">DNS üle HTTPSi</string>
|
<string name="settings_doh_title">DNS üle HTTPSi</string>
|
||||||
<string name="settings_doh_description">Väldi mõnes riigis DNSi mürgitamist</string>
|
<string name="settings_doh_description">Väldi mõnes riigis DNSi mürgitamist</string>
|
||||||
|
|
||||||
@@ -182,6 +204,7 @@
|
|||||||
<string name="flashing">Välgutamine…</string>
|
<string name="flashing">Välgutamine…</string>
|
||||||
<string name="done">Valmis!</string>
|
<string name="done">Valmis!</string>
|
||||||
<string name="failure">Ebaõnnestus</string>
|
<string name="failure">Ebaõnnestus</string>
|
||||||
|
<string name="hide_app_title">Magiski rakenduse peitmine…</string>
|
||||||
<string name="open_link_failed_toast">Lingi avamiseks sobivat rakendust ei leitud.</string>
|
<string name="open_link_failed_toast">Lingi avamiseks sobivat rakendust ei leitud.</string>
|
||||||
<string name="complete_uninstall">Täielik eemaldus</string>
|
<string name="complete_uninstall">Täielik eemaldus</string>
|
||||||
<string name="restore_img">Taasta tõmmised</string>
|
<string name="restore_img">Taasta tõmmised</string>
|
||||||
@@ -190,11 +213,22 @@
|
|||||||
<string name="restore_fail">Originaalne varundus puudub!</string>
|
<string name="restore_fail">Originaalne varundus puudub!</string>
|
||||||
<string name="setup_fail">Seadistus ebaõnnnestus</string>
|
<string name="setup_fail">Seadistus ebaõnnnestus</string>
|
||||||
<string name="env_fix_title">Vajab lisaseadistust</string>
|
<string name="env_fix_title">Vajab lisaseadistust</string>
|
||||||
|
<string name="env_fix_msg">Sinu seade vajab Magiski korralikuks toimimiseks lisaseadistust. Kas soovid jätkata ning seadme taaskäivitada?</string>
|
||||||
<string name="setup_msg">Käivitan keskkonnaseadistust…</string>
|
<string name="setup_msg">Käivitan keskkonnaseadistust…</string>
|
||||||
<string name="authenticate">Autendi</string>
|
<string name="authenticate">Autendi</string>
|
||||||
<string name="unsupport_magisk_title">Mittetoetatud Magiski versioon</string>
|
<string name="unsupport_magisk_title">Mittetoetatud Magiski versioon</string>
|
||||||
|
<string name="unsupport_magisk_msg">See rakenduse versioon ei toeta Magiski versioone, mis on vanemad kui %1$s.\n\nRakendus käitub nii, nagu Magisk ei olekski installitud, palun täienda Magiskit esimesel võimalusel.</string>
|
||||||
|
<string name="unsupport_general_title">Ebanormaalne seisund</string>
|
||||||
|
<string name="unsupport_system_app_msg">Selle rakenduse käitamine süsteemirakendusena ei ole toetatud. Palun taasta see kasutajarakenduseks.</string>
|
||||||
|
<string name="unsupport_other_su_msg">Tuvastati \"su\"-binaar, mida ei installinud Magisk. Palun eemalda mistahes konkureeriv juurkasutaja paigaldus ja/või taasinstalli Magisk.</string>
|
||||||
|
<string name="unsupport_external_storage_msg">Magisk on paigaldatud välismällu. Palun liiguta rakendus sisemällu.</string>
|
||||||
|
<string name="unsupport_nonroot_stub_msg">Peidetud Magiski rakendus ei saa enam töötada, kuna juurkasutaja on kadunud. Palun taasta originaalne APK.</string>
|
||||||
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Selle funktsionaalsuse lubamiseks anna mäluruumi haldamise luba</string>
|
<string name="external_rw_permission_denied">Selle funktsionaalsuse lubamiseks anna mäluruumi haldamise luba</string>
|
||||||
<string name="add_shortcut_title">Lisa avakuvale otsetee</string>
|
<string name="add_shortcut_title">Lisa avakuvale otsetee</string>
|
||||||
|
<string name="add_shortcut_msg">Pärast selle rakenduse peitmist võib selle nimi ja ikoon olla raskesti tuvastatav. Kas soovid avakuvale lisada ilusa otsetee?</string>
|
||||||
<string name="app_not_found">Selle tegevuse teostamiseks ei leitud ühtegi rakendust</string>
|
<string name="app_not_found">Selle tegevuse teostamiseks ei leitud ühtegi rakendust</string>
|
||||||
|
<string name="reboot_apply_change">Muudatuste rakendamiseks taaskäivita</string>
|
||||||
|
<string name="restore_app_confirmation">See taastab peidetud rakenduse uuesti originaalseks rakenduseks. Kas soovid tõesti seda teha?</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -23,16 +23,17 @@
|
|||||||
<string name="home_notice_content">Scarica Magisk SOLTANTO dalla pagina GitHub ufficiale. I file provenienti da fonti sconosciute possono essere dannosi!</string>
|
<string name="home_notice_content">Scarica Magisk SOLTANTO dalla pagina GitHub ufficiale. I file provenienti da fonti sconosciute possono essere dannosi!</string>
|
||||||
<string name="home_support_title">Supportaci</string>
|
<string name="home_support_title">Supportaci</string>
|
||||||
<string name="home_item_source">Codice sorgente</string>
|
<string name="home_item_source">Codice sorgente</string>
|
||||||
<string name="home_support_content">Magisk è e sempre sarà gratuito ed open source. Puoi comunque mostrarci il tuo apprezzamento inviando una piccola donazione.</string>
|
<string name="home_support_content">Magisk è, e sempre sarà, gratuito ed open source. Puoi comunque mostrarci il tuo apprezzamento facendo una donazione.</string>
|
||||||
<string name="home_installed_version">Installata</string>
|
<string name="home_installed_version">Installata</string>
|
||||||
<string name="home_latest_version">Ultima</string>
|
<string name="home_latest_version">Ultima</string>
|
||||||
<string name="invalid_update_channel">Canale di aggiornamento non valido</string>
|
<string name="invalid_update_channel">Canale di aggiornamento non valido</string>
|
||||||
<string name="uninstall_magisk_title">Disinstalla Magisk</string>
|
<string name="uninstall_magisk_title">Disinstalla Magisk</string>
|
||||||
<string name="uninstall_magisk_msg">Tutti i moduli verranno disabilitati/rimossi!\nIl root verrà rimosso e i tuoi dati potrebbero essere criptati, nel caso non lo siano già.</string>
|
<string name="uninstall_magisk_msg">Tutti i moduli verranno disabilitati/rimossi!\nIl root verrà rimosso!\nGli spazi di archiviazione interni decriptati tramite l\'uso di Magisk verranno nuovamente criptati!</string>
|
||||||
|
|
||||||
<!--Install-->
|
<!--Install-->
|
||||||
<string name="keep_force_encryption">Mantieni crittografia forzata</string>
|
<string name="keep_force_encryption">Mantieni crittografia forzata</string>
|
||||||
<string name="keep_dm_verity">Mantieni AVB 2.0/dm-verity</string>
|
<string name="keep_dm_verity">Mantieni AVB 2.0/dm-verity</string>
|
||||||
|
<string name="patch_vbmeta">Correggi vbmeta nell\'immagine di boot</string>
|
||||||
<string name="recovery_mode">Modalità recovery</string>
|
<string name="recovery_mode">Modalità recovery</string>
|
||||||
<string name="install_options_title">Opzioni</string>
|
<string name="install_options_title">Opzioni</string>
|
||||||
<string name="install_method_title">Metodo</string>
|
<string name="install_method_title">Metodo</string>
|
||||||
@@ -87,8 +88,6 @@
|
|||||||
<string name="pid">PID: %1$d</string>
|
<string name="pid">PID: %1$d</string>
|
||||||
<string name="target_uid">Target UID: %1$d</string>
|
<string name="target_uid">Target UID: %1$d</string>
|
||||||
|
|
||||||
<!--SafetyNet-->
|
|
||||||
|
|
||||||
<!-- MagiskHide -->
|
<!-- MagiskHide -->
|
||||||
<string name="show_system_app">Mostra app di sistema</string>
|
<string name="show_system_app">Mostra app di sistema</string>
|
||||||
<string name="show_os_app">Mostra app del sistema operativo</string>
|
<string name="show_os_app">Mostra app del sistema operativo</string>
|
||||||
@@ -130,8 +129,8 @@
|
|||||||
<string name="settings_update_channel_title">Canale di aggiornamento</string>
|
<string name="settings_update_channel_title">Canale di aggiornamento</string>
|
||||||
<string name="settings_update_stable">Stabile</string>
|
<string name="settings_update_stable">Stabile</string>
|
||||||
<string name="settings_update_beta">Beta</string>
|
<string name="settings_update_beta">Beta</string>
|
||||||
<string name="settings_update_custom">Canale personalizzato</string>
|
<string name="settings_update_custom">Personalizzato</string>
|
||||||
<string name="settings_update_custom_msg">Inserisci un URL personalizzato</string>
|
<string name="settings_update_custom_msg">Inserisci l\'URL del canale personalizzato</string>
|
||||||
<string name="settings_zygisk_summary">Esegui parti di Magisk nel processo zygote</string>
|
<string name="settings_zygisk_summary">Esegui parti di Magisk nel processo zygote</string>
|
||||||
<string name="settings_denylist_title">Applica lista di blocco</string>
|
<string name="settings_denylist_title">Applica lista di blocco</string>
|
||||||
<string name="settings_denylist_summary">Tutte le modifiche apportate da Magisk verranno annullate per i processi in lista di blocco</string>
|
<string name="settings_denylist_summary">Tutte le modifiche apportate da Magisk verranno annullate per i processi in lista di blocco</string>
|
||||||
@@ -160,15 +159,15 @@
|
|||||||
<string name="superuser_notification">Notifica di accesso root</string>
|
<string name="superuser_notification">Notifica di accesso root</string>
|
||||||
<string name="settings_su_reauth_title">Riautentica dopo aggiornamento</string>
|
<string name="settings_su_reauth_title">Riautentica dopo aggiornamento</string>
|
||||||
<string name="settings_su_reauth_summary">Richiedi nuovamente i permessi di root dopo l\'aggiornamento di un\'app</string>
|
<string name="settings_su_reauth_summary">Richiedi nuovamente i permessi di root dopo l\'aggiornamento di un\'app</string>
|
||||||
<string name="settings_su_tapjack_title">Abilita protezione anti-tapjacking</string>
|
<string name="settings_su_tapjack_title">Protezione anti-tapjacking</string>
|
||||||
<string name="settings_su_tapjack_summary">La schermata di richiesta dei permessi di root non risponderà al tocco mentre è oscurata da altre finestre o elementi in sovraimpressione</string>
|
<string name="settings_su_tapjack_summary">La schermata di richiesta dei permessi di root non risponderà al tocco mentre è oscurata da altre finestre o elementi in sovraimpressione</string>
|
||||||
<string name="settings_su_biometric_title">Abilita autenticazione biometrica</string>
|
<string name="settings_su_biometric_title">Autenticazione biometrica</string>
|
||||||
<string name="settings_su_biometric_summary">Utilizza l\'autenticazione biometrica per accettare le richieste di accesso root</string>
|
<string name="settings_su_biometric_summary">Utilizza l\'autenticazione biometrica per accettare le richieste di accesso root</string>
|
||||||
<string name="no_biometric">Il dispositivo non è supportato o le impostazioni biometriche sono disattivate</string>
|
<string name="no_biometric">Il dispositivo non è supportato o le impostazioni biometriche sono disattivate</string>
|
||||||
<string name="settings_customization">Personalizzazione</string>
|
<string name="settings_customization">Personalizzazione</string>
|
||||||
<string name="setting_add_shortcut_summary">Aggiungi un collegamento alla schermata iniziale se il nome e l\'icona sono difficili da riconoscere dopo aver nascosto l\'app</string>
|
<string name="setting_add_shortcut_summary">Aggiungi un collegamento alla schermata iniziale se il nome e l\'icona sono difficili da riconoscere dopo aver nascosto l\'app</string>
|
||||||
<string name="settings_doh_title">DNS over HTTPS</string>
|
<string name="settings_doh_title">DNS over HTTPS</string>
|
||||||
<string name="settings_doh_description">Soluzione alternativa al DNS poisoning in alcune nazioni</string>
|
<string name="settings_doh_description">Aggira il DNS poisoning in alcune nazioni</string>
|
||||||
|
|
||||||
<string name="multiuser_mode">Modalità multiutente</string>
|
<string name="multiuser_mode">Modalità multiutente</string>
|
||||||
<string name="settings_owner_only">Solo per il proprietario del dispositivo</string>
|
<string name="settings_owner_only">Solo per il proprietario del dispositivo</string>
|
||||||
@@ -187,11 +186,14 @@
|
|||||||
<string name="isolate_summary">Ogni sessione di root avrà il suo namespace isolato</string>
|
<string name="isolate_summary">Ogni sessione di root avrà il suo namespace isolato</string>
|
||||||
|
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="update_channel">Aggiornamenti Magisk</string>
|
<string name="update_channel">Aggiornamenti di Magisk</string>
|
||||||
<string name="progress_channel">Notifiche di avanzamento</string>
|
<string name="progress_channel">Notifiche di avanzamento</string>
|
||||||
|
<string name="updated_channel">Aggiornamento completato</string>
|
||||||
<string name="download_complete">Download completato</string>
|
<string name="download_complete">Download completato</string>
|
||||||
<string name="download_file_error">Errore durante il download del file</string>
|
<string name="download_file_error">Errore durante il download del file</string>
|
||||||
<string name="magisk_update_title">È disponibile un aggiornamento di Magisk!</string>
|
<string name="magisk_update_title">È disponibile un aggiornamento di Magisk!</string>
|
||||||
|
<string name="updated_title">Magisk è stato aggiornato</string>
|
||||||
|
<string name="updated_text">Tocca per aprire l\'app</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="yes">Sì</string>
|
<string name="yes">Sì</string>
|
||||||
@@ -216,14 +218,15 @@
|
|||||||
<string name="setup_msg">Configurazione dell\'ambiente in corso…</string>
|
<string name="setup_msg">Configurazione dell\'ambiente in corso…</string>
|
||||||
<string name="authenticate">Autentica</string>
|
<string name="authenticate">Autentica</string>
|
||||||
<string name="unsupport_magisk_title">Versione di Magisk non supportata</string>
|
<string name="unsupport_magisk_title">Versione di Magisk non supportata</string>
|
||||||
<string name="unsupport_magisk_msg">Questa versione dell\'app non supporta versioni di Magisk inferiori a %1$s.\n\nL\'app si comporterà come se Magisk non fosse installato: aggiornalo il prima possibile.</string>
|
<string name="unsupport_magisk_msg">Questa versione dell\'app non supporta le versioni di Magisk inferiori a %1$s.\n\nL\'app si comporterà come se Magisk non fosse installato: aggiornalo il prima possibile.</string>
|
||||||
<string name="unsupport_general_title">Stato anomalo rilevato</string>
|
<string name="unsupport_general_title">Stato anomalo rilevato</string>
|
||||||
<string name="unsupport_system_app_msg">L\'esecuzione di quest\'app come app di sistema non è supportata. Reinstallala come applicazione utente.</string>
|
<string name="unsupport_system_app_msg">L\'esecuzione di quest\'app come app di sistema non è supportata. Reinstallala come applicazione utente.</string>
|
||||||
<string name="unsupport_other_su_msg">È stato rilevato un comando \"su\" che non appartiene a Magisk. Rimuovilo per evitare malfunzionamenti.</string>
|
<string name="unsupport_other_su_msg">È stato rilevato un binario \"su\" che non appartiene a Magisk. Rimuovi qualsiasi altro sistema di root e/o reinstalla Magisk.</string>
|
||||||
<string name="unsupport_external_storage_msg">Magisk è installato nella memoria esterna. Sposta l\'app nella memoria interna.</string>
|
<string name="unsupport_external_storage_msg">Magisk è installato nella memoria esterna. Sposta l\'app nella memoria interna.</string>
|
||||||
<string name="unsupport_nonroot_stub_msg">Dal momento che i permessi di root sono stati persi, l\'app non può più funzionare rimanendo nascosta. Ripristina l\'APK originale.</string>
|
<string name="unsupport_nonroot_stub_msg">Dal momento che i permessi di root sono stati persi, l\'app di Magisk nascosta non può più funzionare. Ripristina l\'APK originale.</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Consenti l\'accesso alla memoria del dispositivo per abilitare questa opzione</string>
|
<string name="external_rw_permission_denied">Consenti l\'accesso alla memoria del dispositivo per abilitare questa opzione</string>
|
||||||
|
<string name="install_unknown_denied">Consenti l\'installazione di app sconosciute per abilitare questa funzionalità</string>
|
||||||
<string name="add_shortcut_title">Aggiungi collegamento alla schermata iniziale</string>
|
<string name="add_shortcut_title">Aggiungi collegamento alla schermata iniziale</string>
|
||||||
<string name="add_shortcut_msg">Dopo aver nascosto quest\'app, il suo nome e la sua icona potrebbero diventare difficili da riconoscere. Vuoi aggiungere una scorciatoia alla schermata principale?</string>
|
<string name="add_shortcut_msg">Dopo aver nascosto quest\'app, il suo nome e la sua icona potrebbero diventare difficili da riconoscere. Vuoi aggiungere una scorciatoia alla schermata principale?</string>
|
||||||
<string name="app_not_found">Non è stata trovata un\'app per gestire questa azione</string>
|
<string name="app_not_found">Non è stata trovata un\'app per gestire questa azione</string>
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
<!--Install-->
|
<!--Install-->
|
||||||
<string name="keep_force_encryption">Ponechať vynútené šifrovanie</string>
|
<string name="keep_force_encryption">Ponechať vynútené šifrovanie</string>
|
||||||
<string name="keep_dm_verity">Ponechať AVB 2.0/dm-verity</string>
|
<string name="keep_dm_verity">Ponechať AVB 2.0/dm-verity</string>
|
||||||
|
<string name="patch_vbmeta">Zaplátať vbmeta v boot image</string>
|
||||||
<string name="recovery_mode">Režim Recovery</string>
|
<string name="recovery_mode">Režim Recovery</string>
|
||||||
<string name="install_options_title">Možnosti</string>
|
<string name="install_options_title">Možnosti</string>
|
||||||
<string name="install_method_title">Metóda</string>
|
<string name="install_method_title">Metóda</string>
|
||||||
@@ -189,9 +190,12 @@
|
|||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="update_channel">Aktualizácie Magisku</string>
|
<string name="update_channel">Aktualizácie Magisku</string>
|
||||||
<string name="progress_channel">Upozornenia o priebehu</string>
|
<string name="progress_channel">Upozornenia o priebehu</string>
|
||||||
|
<string name="updated_channel">Aktualizácia dokončená</string>
|
||||||
<string name="download_complete">Sťahovanie ukončené</string>
|
<string name="download_complete">Sťahovanie ukončené</string>
|
||||||
<string name="download_file_error">Chyba sťahovania súboru</string>
|
<string name="download_file_error">Chyba sťahovania súboru</string>
|
||||||
<string name="magisk_update_title">Je dostupná aktualizácia Magisku!</string>
|
<string name="magisk_update_title">Je dostupná aktualizácia Magisku!</string>
|
||||||
|
<string name="updated_title">Magisk aktualizovaný</string>
|
||||||
|
<string name="updated_text">Ťuknutím otvoríte apku</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="yes">Áno</string>
|
<string name="yes">Áno</string>
|
||||||
@@ -224,6 +228,7 @@
|
|||||||
<string name="unsupport_nonroot_stub_msg">Skrytá aplikácia Magisk nemôže pokračovať v práci, lebo root sa stratil. Prosím, obnovte ju z pôvodného súboru APK.</string>
|
<string name="unsupport_nonroot_stub_msg">Skrytá aplikácia Magisk nemôže pokračovať v práci, lebo root sa stratil. Prosím, obnovte ju z pôvodného súboru APK.</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Udeľte povolenie pre povolenie tejto funkcie</string>
|
<string name="external_rw_permission_denied">Udeľte povolenie pre povolenie tejto funkcie</string>
|
||||||
|
<string name="install_unknown_denied">Povoľte "inštaláciu neznámych aplikácii" na povolenie tejto funkcie</string>
|
||||||
<string name="add_shortcut_title">Pridať odkaz na domovskú obrazovku</string>
|
<string name="add_shortcut_title">Pridať odkaz na domovskú obrazovku</string>
|
||||||
<string name="add_shortcut_msg">Po skrytí tejto aplikácie sa jej názov a ikona môžu stať ťažko rozoznateľnými. Chcete pridať krajší odkaz na domovskú obrazovku?</string>
|
<string name="add_shortcut_msg">Po skrytí tejto aplikácie sa jej názov a ikona môžu stať ťažko rozoznateľnými. Chcete pridať krajší odkaz na domovskú obrazovku?</string>
|
||||||
<string name="app_not_found">Nenašla sa žiadna aplikácia, ktorá dokáže spracovať túto akciu</string>
|
<string name="app_not_found">Nenašla sa žiadna aplikácia, ktorá dokáže spracovať túto akciu</string>
|
||||||
|
@@ -106,7 +106,7 @@
|
|||||||
<string name="module_version_author">%1$s nga %2$s</string>
|
<string name="module_version_author">%1$s nga %2$s</string>
|
||||||
<string name="module_state_remove">Hiqe</string>
|
<string name="module_state_remove">Hiqe</string>
|
||||||
<string name="module_state_restore">Rikëthe</string>
|
<string name="module_state_restore">Rikëthe</string>
|
||||||
<string name="module_action_install_external">Instaloni nga ruajtja</string>
|
<string name="module_action_install_external">Instaloni nga sdcard</string>
|
||||||
<string name="update_available">Përditësimi në dispozicion</string>
|
<string name="update_available">Përditësimi në dispozicion</string>
|
||||||
<string name="suspend_text_riru">Moduli u pezullua sepse %1$s është aktivizuar</string>
|
<string name="suspend_text_riru">Moduli u pezullua sepse %1$s është aktivizuar</string>
|
||||||
<string name="suspend_text_zygisk">Moduli është pezulluar sepse %1$s nuk është i aktivizuar</string>
|
<string name="suspend_text_zygisk">Moduli është pezulluar sepse %1$s nuk është i aktivizuar</string>
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
<string name="settings_update_beta">Beta</string>
|
<string name="settings_update_beta">Beta</string>
|
||||||
<string name="settings_update_custom">Kanal me porosi</string>
|
<string name="settings_update_custom">Kanal me porosi</string>
|
||||||
<string name="settings_update_custom_msg">Fut një URL të personalizuar</string>
|
<string name="settings_update_custom_msg">Fut një URL të personalizuar</string>
|
||||||
<string name="settings_zygisk_summary">Drejtoni pjesë të Magisk në demonin zigotë</string>
|
<string name="settings_zygisk_summary">Drejtoni pjesë të Magisk në demonin zygote</string>
|
||||||
<string name="settings_denylist_title">Zbato Listën e Mohimit</string>
|
<string name="settings_denylist_title">Zbato Listën e Mohimit</string>
|
||||||
<string name="settings_denylist_summary">Proceset në listën e mohimit do të kenë të gjitha modifikimet e Magisk</string>
|
<string name="settings_denylist_summary">Proceset në listën e mohimit do të kenë të gjitha modifikimet e Magisk</string>
|
||||||
<string name="settings_denylist_error">Ky funksion kërkon %1$s për tu aktivizuar</string>
|
<string name="settings_denylist_error">Ky funksion kërkon %1$s për tu aktivizuar</string>
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
<string name="settings_su_request_60">60 Sekonda</string>
|
<string name="settings_su_request_60">60 Sekonda</string>
|
||||||
<string name="superuser_access">Aksesi i Super-përdorues</string>
|
<string name="superuser_access">Aksesi i Super-përdorues</string>
|
||||||
<string name="auto_response">Përgjigje automatike</string>
|
<string name="auto_response">Përgjigje automatike</string>
|
||||||
<string name="request_timeout">Kërkesës i mbaroi koha</string>
|
<string name="request_timeout">Koha për mbarimit të Kërkesës</string>
|
||||||
<string name="superuser_notification">Njoftimi i Super-përdoruesit</string>
|
<string name="superuser_notification">Njoftimi i Super-përdoruesit</string>
|
||||||
<string name="settings_su_reauth_title">Ri-vërtetimi pas azhurnimit</string>
|
<string name="settings_su_reauth_title">Ri-vërtetimi pas azhurnimit</string>
|
||||||
<string name="settings_su_reauth_summary">Ri-vërtetoni lejet e super-përdoruesit pas azhurnimit të aplikacionit</string>
|
<string name="settings_su_reauth_summary">Ri-vërtetoni lejet e super-përdoruesit pas azhurnimit të aplikacionit</string>
|
||||||
@@ -187,10 +187,13 @@
|
|||||||
|
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="update_channel">Përditësimet e magisk</string>
|
<string name="update_channel">Përditësimet e magisk</string>
|
||||||
|
<string name="updated_channel">Përditësimi përfundoi</string>
|
||||||
<string name="progress_channel">Njoftimet e progresit</string>
|
<string name="progress_channel">Njoftimet e progresit</string>
|
||||||
<string name="download_complete">Shkarkimi përfundoi</string>
|
<string name="download_complete">Shkarkimi përfundoi</string>
|
||||||
<string name="download_file_error">Gabim në shkarkimin e skedarit</string>
|
<string name="download_file_error">Gabim në shkarkimin e skedarit</string>
|
||||||
<string name="magisk_update_title">Përditësimi Magisk i disponueshëm!</string>
|
<string name="magisk_update_title">Përditësimi Magisk i disponueshëm!</string>
|
||||||
|
<string name="updated_title">Magisk u përditësua</string>
|
||||||
|
<string name="updated_text">Prekni për të hapur aplikacionin</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="yes">Po</string>
|
<string name="yes">Po</string>
|
||||||
@@ -223,6 +226,7 @@
|
|||||||
<string name="unsupport_nonroot_stub_msg">Aplikacioni nuk mund të vazhdojë të punojë në gjendjen e fshehur pasi rrënja ishte e humbur. Ju lutemi rivendoseni përsëri në APK-në origjinale.</string>
|
<string name="unsupport_nonroot_stub_msg">Aplikacioni nuk mund të vazhdojë të punojë në gjendjen e fshehur pasi rrënja ishte e humbur. Ju lutemi rivendoseni përsëri në APK-në origjinale.</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Jepni lejen e ruajtjes për të aktivizuar këtë funksion</string>
|
<string name="external_rw_permission_denied">Jepni lejen e ruajtjes për të aktivizuar këtë funksion</string>
|
||||||
|
<string name="install_unknown_denied">Lejo "instaloni aplikacione të panjohura" për të aktivizuar këtë funksion</string>
|
||||||
<string name="add_shortcut_title">Shto shkurtore në ekranin bazë</string>
|
<string name="add_shortcut_title">Shto shkurtore në ekranin bazë</string>
|
||||||
<string name="add_shortcut_msg">Pas fshehjes së këtij aplikacioni, emri dhe ikona e tij mund të bëhen të vështira për tu njohur. Dëshironi të shtoni një shkurtore mjaft të bukur në ekranin bazë?</string>
|
<string name="add_shortcut_msg">Pas fshehjes së këtij aplikacioni, emri dhe ikona e tij mund të bëhen të vështira për tu njohur. Dëshironi të shtoni një shkurtore mjaft të bukur në ekranin bazë?</string>
|
||||||
<string name="app_not_found">Asnjë aplikacion nuk u gjet për të trajtuar këtë veprim</string>
|
<string name="app_not_found">Asnjë aplikacion nuk u gjet për të trajtuar këtë veprim</string>
|
||||||
|
@@ -8,12 +8,12 @@
|
|||||||
<string name="install">ติดตั้ง</string>
|
<string name="install">ติดตั้ง</string>
|
||||||
|
|
||||||
<!--Status Fragment-->
|
<!--Status Fragment-->
|
||||||
<string name="invalid_update_channel">ช่องทางการอัพเดตไม่ถูกต้อง</string>
|
<string name="invalid_update_channel">ช่องทางการอัปเดตไม่ถูกต้อง</string>
|
||||||
<string name="keep_force_encryption">เก็บค่าบังคับการเข้ารหัส</string>
|
<string name="keep_force_encryption">เก็บค่าบังคับการเข้ารหัส</string>
|
||||||
<string name="keep_dm_verity">เก็บค่า AVB 2.0/dm-verity</string>
|
<string name="keep_dm_verity">เก็บค่า AVB 2.0/dm-verity</string>
|
||||||
<string name="uninstall_magisk_title">ถอนการติดตั้ง Magisk</string>
|
<string name="uninstall_magisk_title">ถอนการติดตั้ง Magisk</string>
|
||||||
<string name="uninstall_magisk_msg">โมดูลทั้งหมดจะถูกปิดการใช้งาน/ถูกลบ, สิทธิการเข้าถึง Root จะถูกลบ และข้อมูลของคุณอาจถูกเข้ารหัส</string>
|
<string name="uninstall_magisk_msg">โมดูลทั้งหมดจะถูกปิดการใช้งาน/ถูกลบ, สิทธิการเข้าถึง Root จะถูกลบ และข้อมูลของคุณอาจถูกเข้ารหัส</string>
|
||||||
<string name="update">อัพเดต</string>
|
<string name="update">อัปเดต</string>
|
||||||
|
|
||||||
<!--Module Fragment-->
|
<!--Module Fragment-->
|
||||||
<string name="no_info_provided">(ไม่มีข้อมูล)</string>
|
<string name="no_info_provided">(ไม่มีข้อมูล)</string>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<string name="reboot_download">รีบู๊ตไป Download</string>
|
<string name="reboot_download">รีบู๊ตไป Download</string>
|
||||||
|
|
||||||
<!--Repo Fragment-->
|
<!--Repo Fragment-->
|
||||||
<string name="update_available">มีการอัพเดต</string>
|
<string name="update_available">มีการอัปเดต</string>
|
||||||
<string name="home_installed_version">ติดตั้งแล้ว</string>
|
<string name="home_installed_version">ติดตั้งแล้ว</string>
|
||||||
|
|
||||||
<!--Log Fragment-->
|
<!--Log Fragment-->
|
||||||
@@ -34,17 +34,17 @@
|
|||||||
<string name="app_changelog">สิ่งที่เพิ่มใหม่</string>
|
<string name="app_changelog">สิ่งที่เพิ่มใหม่</string>
|
||||||
|
|
||||||
<!-- System Components, Notifications -->
|
<!-- System Components, Notifications -->
|
||||||
<string name="update_channel">การอัพเดต Magisk</string>
|
<string name="update_channel">การอัปเดต Magisk</string>
|
||||||
<string name="progress_channel">สถานะ</string>
|
<string name="progress_channel">สถานะ</string>
|
||||||
<string name="download_complete">ดาวน์โหลดสำเร็จ</string>
|
<string name="download_complete">ดาวน์โหลดสำเร็จ</string>
|
||||||
<string name="download_file_error">เกิดข้อผิดพลาดในการดาวน์โหลดไฟล์</string>
|
<string name="download_file_error">เกิดข้อผิดพลาดในการดาวน์โหลดไฟล์</string>
|
||||||
<string name="magisk_update_title">มีการอัพเดต Magisk!</string>
|
<string name="magisk_update_title">มีการอัปเดต Magisk!</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="repo_install_title">ติดตั้ง %1$s %2$s(%3$d)</string>
|
<string name="repo_install_title">ติดตั้ง %1$s %2$s(%3$d)</string>
|
||||||
<string name="download">ดาวน์โหลด</string>
|
<string name="download">ดาวน์โหลด</string>
|
||||||
<string name="reboot">รีบู๊ต</string>
|
<string name="reboot">รีบู๊ต</string>
|
||||||
<string name="release_notes">-้อมูลเพิ่มเติม</string>
|
<string name="release_notes">ข้อมูลเพิ่มเติม</string>
|
||||||
<string name="manager_download_install">แตะเพื่อดาวน์โหลดและติดตั้ง</string>
|
<string name="manager_download_install">แตะเพื่อดาวน์โหลดและติดตั้ง</string>
|
||||||
<string name="flashing">กำลังแฟลช</string>
|
<string name="flashing">กำลังแฟลช</string>
|
||||||
<string name="open_link_failed_toast">ไม่พบแอปพลิเคชันสำหรับเปิดลิ้งค์</string>
|
<string name="open_link_failed_toast">ไม่พบแอปพลิเคชันสำหรับเปิดลิ้งค์</string>
|
||||||
@@ -64,9 +64,9 @@
|
|||||||
<!--Settings Activity -->
|
<!--Settings Activity -->
|
||||||
<string name="language">ภาษา</string>
|
<string name="language">ภาษา</string>
|
||||||
<string name="system_default">(มาตรฐานระบบ)</string>
|
<string name="system_default">(มาตรฐานระบบ)</string>
|
||||||
<string name="settings_check_update_title">ตรวจสอบการอัพเดต</string>
|
<string name="settings_check_update_title">ตรวจสอบการอัปเดต</string>
|
||||||
<string name="settings_check_update_summary">ตรวจสอบการอัพเดตเป็นระยะในพื้นหลัง</string>
|
<string name="settings_check_update_summary">ตรวจสอบการอัปเดตเป็นระยะในพื้นหลัง</string>
|
||||||
<string name="settings_update_channel_title">ช่องทางการอัพเดต</string>
|
<string name="settings_update_channel_title">ช่องทางการอัปเดต</string>
|
||||||
<string name="settings_update_stable">เสถียร</string>
|
<string name="settings_update_stable">เสถียร</string>
|
||||||
<string name="settings_update_beta">เบต้า</string>
|
<string name="settings_update_beta">เบต้า</string>
|
||||||
<string name="settings_update_custom">กำหนดเอง</string>
|
<string name="settings_update_custom">กำหนดเอง</string>
|
||||||
@@ -89,8 +89,8 @@
|
|||||||
<string name="auto_response">การตอบกลับ</string>
|
<string name="auto_response">การตอบกลับ</string>
|
||||||
<string name="request_timeout">เวลาการขอใช้งาน</string>
|
<string name="request_timeout">เวลาการขอใช้งาน</string>
|
||||||
<string name="superuser_notification">การแจ้งเตือน Superuser</string>
|
<string name="superuser_notification">การแจ้งเตือน Superuser</string>
|
||||||
<string name="settings_su_reauth_title">ขอสิทธิ์ใหม่หลังจากอัพเกรด</string>
|
<string name="settings_su_reauth_title">ขอสิทธิ์ใหม่หลังจากอัปเกรด</string>
|
||||||
<string name="settings_su_reauth_summary">ขอสิทธิ์ superuser ใหม่หลังจากแอปถูกอัพเกรด</string>
|
<string name="settings_su_reauth_summary">ขอสิทธิ์ superuser ใหม่หลังจากแอปถูกอัปเกรด</string>
|
||||||
|
|
||||||
<string name="multiuser_mode">โหมดผู้ใช้หลายคน</string>
|
<string name="multiuser_mode">โหมดผู้ใช้หลายคน</string>
|
||||||
<string name="settings_owner_only">เจ้าของอุปกรณ์เท่านั้น</string>
|
<string name="settings_owner_only">เจ้าของอุปกรณ์เท่านั้น</string>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<string name="grant">อนุญาต</string>
|
<string name="grant">อนุญาต</string>
|
||||||
<string name="su_warning">จะให้การเข้าถึงอุปกรณ์ในทุกรูปแบบ กรุณาปฏิเสธหากคุณไม่มั่นใจ!</string>
|
<string name="su_warning">จะให้การเข้าถึงอุปกรณ์ในทุกรูปแบบ กรุณาปฏิเสธหากคุณไม่มั่นใจ!</string>
|
||||||
<string name="forever">ตลอดกาล</string>
|
<string name="forever">ตลอดกาล</string>
|
||||||
<string name="once">คั้งเดียว</string>
|
<string name="once">ครั้งเดียว</string>
|
||||||
<string name="tenmin">10 นาที</string>
|
<string name="tenmin">10 นาที</string>
|
||||||
<string name="twentymin">20 นาที</string>
|
<string name="twentymin">20 นาที</string>
|
||||||
<string name="thirtymin">30 นาที</string>
|
<string name="thirtymin">30 นาที</string>
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<string name="logs">Nhật ký</string>
|
<string name="logs">Nhật ký</string>
|
||||||
<string name="settings">Cài đặt</string>
|
<string name="settings">Cài đặt</string>
|
||||||
<string name="install">Cài đặt</string>
|
<string name="install">Cài đặt</string>
|
||||||
<string name="section_home">Home</string>
|
<string name="section_home">Trang chủ</string>
|
||||||
<string name="section_theme">Chủ đề</string>
|
<string name="section_theme">Chủ đề</string>
|
||||||
<string name="denylist">DenyList</string>
|
<string name="denylist">DenyList</string>
|
||||||
|
|
||||||
@@ -23,16 +23,17 @@
|
|||||||
<string name="home_notice_content">Chỉ nên tải Magisk từ trang GitHub chính thức. Tải tệp từ các nguồn không rõ có thể gây hại!</string>
|
<string name="home_notice_content">Chỉ nên tải Magisk từ trang GitHub chính thức. Tải tệp từ các nguồn không rõ có thể gây hại!</string>
|
||||||
<string name="home_support_title">Hỗ trợ chúng tôi</string>
|
<string name="home_support_title">Hỗ trợ chúng tôi</string>
|
||||||
<string name="home_item_source">Nguồn</string>
|
<string name="home_item_source">Nguồn</string>
|
||||||
<string name="home_support_content">Magisk, và sẽ luôn luôn là, miễn phí và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách gửi một khoản đóng góp nhỏ.</string>
|
<string name="home_support_content">Magisk sẽ, và luôn luôn là, miễn phí và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách gửi một khoản đóng góp nhỏ.</string>
|
||||||
<string name="home_installed_version">Cài đặt</string>
|
<string name="home_installed_version">Cài đặt</string>
|
||||||
<string name="home_latest_version">Mới nhất</string>
|
<string name="home_latest_version">Mới nhất</string>
|
||||||
<string name="invalid_update_channel">Kênh cập nhật không hợp lệ</string>
|
<string name="invalid_update_channel">Kênh cập nhật không hợp lệ</string>
|
||||||
<string name="uninstall_magisk_title">Gỡ cài đặt Magisk</string>
|
<string name="uninstall_magisk_title">Gỡ cài đặt Magisk</string>
|
||||||
<string name="uninstall_magisk_msg">Tất cả các mô-đun sẽ bị tắt hoặc bị xóa!\nRoot sẽ bị gỡ bỏ\nDữ liệu của bạn có thể bị mã hóa lại nếu nó không mã hóa!</string>
|
<string name="uninstall_magisk_msg">Tất cả các mô-đun sẽ bị tắt hoặc bị xóa!\nRoot sẽ bị gỡ bỏ\nDữ liệu của bạn có thể bị mã hóa lại nếu nó không mã hóa!</string>
|
||||||
|
|
||||||
<!--Settings-->
|
<!--Install-->
|
||||||
<string name="keep_force_encryption">Giữ bắt buộc mã hóa</string>
|
<string name="keep_force_encryption">Giữ bắt buộc mã hóa</string>
|
||||||
<string name="keep_dm_verity">Giữ AVB 2.0/dm-verity</string>
|
<string name="keep_dm_verity">Giữ AVB 2.0/dm-verity</string>
|
||||||
|
<string name="patch_vbmeta">Vá vbmeta trong boot image</string>
|
||||||
<string name="recovery_mode">Chế độ Recovery</string>
|
<string name="recovery_mode">Chế độ Recovery</string>
|
||||||
<string name="install_options_title">Tùy chọn</string>
|
<string name="install_options_title">Tùy chọn</string>
|
||||||
<string name="install_method_title">Phương pháp</string>
|
<string name="install_method_title">Phương pháp</string>
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
<string name="manager_download_install">Nhấn để tải xuống và cài đặt</string>
|
<string name="manager_download_install">Nhấn để tải xuống và cài đặt</string>
|
||||||
<string name="direct_install">Cài đặt trực tiếp (Khuyến nghị)</string>
|
<string name="direct_install">Cài đặt trực tiếp (Khuyến nghị)</string>
|
||||||
<string name="install_inactive_slot">Cài đặt vào vùng không hoạt động (Sau OTA)</string>
|
<string name="install_inactive_slot">Cài đặt vào vùng không hoạt động (Sau OTA)</string>
|
||||||
<string name="install_inactive_slot_msg">Thiết bị của bạn sẽ BẮT BUỘC khởi động vào vị trí không hoạt động hiện tại sau khi khởi động lại!\nChỉ sử dụng tùy chọn này sau khi hoàn tất OTA.\nTiếp tục chứ?</string>
|
<string name="install_inactive_slot_msg">Thiết bị của bạn sẽ bị BUỘC khởi động vào vị trí không hoạt động hiện tại sau khi khởi động lại!\nChỉ sử dụng tùy chọn này sau khi hoàn tất OTA.\nTiếp tục chứ?</string>
|
||||||
<string name="setup_title">Thiết lập bổ sung</string>
|
<string name="setup_title">Thiết lập bổ sung</string>
|
||||||
<string name="select_patch_file">Chọn và vá tệp</string>
|
<string name="select_patch_file">Chọn và vá tệp</string>
|
||||||
<string name="patch_file_msg">Chọn đĩa ảnh (*.img) hoặc tệp tarfile ODIN (*.tar)</string>
|
<string name="patch_file_msg">Chọn đĩa ảnh (*.img) hoặc tệp tarfile ODIN (*.tar)</string>
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
|
|
||||||
<!--Superuser-->
|
<!--Superuser-->
|
||||||
<string name="su_request_title">Yêu cầu Superuser</string>
|
<string name="su_request_title">Yêu cầu Superuser</string>
|
||||||
<string name="touch_filtered_warning">Vì một ứng dụng đang che phủ yêu cầu Superuser, Magisk không thể xác minh đó là phản hồi của bạn</string>
|
<string name="touch_filtered_warning">Vì một ứng dụng đang che phủ yêu cầu Superuser, Magisk không thể xác minh phản hồi của bạn</string>
|
||||||
<string name="deny">Từ chối</string>
|
<string name="deny">Từ chối</string>
|
||||||
<string name="prompt">Nhắc nhở</string>
|
<string name="prompt">Nhắc nhở</string>
|
||||||
<string name="grant">Cấp phép</string>
|
<string name="grant">Cấp phép</string>
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
<string name="superuser_policy_none">Chưa có ứng dụng nào yêu cầu Superuser.</string>
|
<string name="superuser_policy_none">Chưa có ứng dụng nào yêu cầu Superuser.</string>
|
||||||
|
|
||||||
<!--Logs-->
|
<!--Logs-->
|
||||||
<string name="log_data_none">Nhật ký của bạn đang trống, hãy thử sử dụng các ứng dụng hỗ trợ SU của bạn nhiều hơn</string>
|
<string name="log_data_none">Nhật ký của bạn đang trống, hãy thử sử dụng các ứng dụng root của bạn nhiều hơn</string>
|
||||||
<string name="log_data_magisk_none">Nhật ký Magisk trống, điều đó thật kỳ lạ</string>
|
<string name="log_data_magisk_none">Nhật ký Magisk trống, điều đó thật kỳ lạ</string>
|
||||||
<string name="menuSaveLog">Lưu nhật ký</string>
|
<string name="menuSaveLog">Lưu nhật ký</string>
|
||||||
<string name="menuClearLog">Xóa nhật ký ngay bây giờ</string>
|
<string name="menuClearLog">Xóa nhật ký ngay bây giờ</string>
|
||||||
@@ -102,14 +103,15 @@
|
|||||||
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
|
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
|
||||||
<string name="reboot_download">Khởi động lại vào Download</string>
|
<string name="reboot_download">Khởi động lại vào Download</string>
|
||||||
<string name="reboot_edl">Khởi động lại vào EDL</string>
|
<string name="reboot_edl">Khởi động lại vào EDL</string>
|
||||||
<string name="module_version_author">%1$s x %2$s</string>
|
<string name="module_version_author">%1$s bởi %2$s</string>
|
||||||
<string name="module_state_remove">Loại bỏ</string>
|
<string name="module_state_remove">Gỡ bỏ</string>
|
||||||
<string name="module_state_restore">Khôi phục</string>
|
<string name="module_state_restore">Khôi phục</string>
|
||||||
<string name="module_action_install_external">Cài đặt từ bộ nhớ</string>
|
<string name="module_action_install_external">Cài đặt từ bộ nhớ</string>
|
||||||
<string name="update_available">Cập nhật có sẵn</string>
|
<string name="update_available">Cập nhật có sẵn</string>
|
||||||
<string name="suspend_text_riru">Mô-đun bị vô hiệu hóa vì %1$s được bật</string>
|
<string name="suspend_text_riru">Mô-đun bị vô hiệu hóa vì %1$s được bật</string>
|
||||||
<string name="suspend_text_zygisk">Mô-đun bị vô hiệu hóa vì %1$s không được bật</string>
|
<string name="suspend_text_zygisk">Mô-đun bị vô hiệu hóa vì %1$s không được bật</string>
|
||||||
|
<string name="zygisk_module_unloaded">Không tải mô-đun Zygisk vì không tương thích</string>
|
||||||
|
|
||||||
<!--Settings-->
|
<!--Settings-->
|
||||||
<string name="settings_dark_mode_title">Chế độ chủ đề</string>
|
<string name="settings_dark_mode_title">Chế độ chủ đề</string>
|
||||||
<string name="settings_dark_mode_message">Chọn chế độ phù hợp nhất với phong cách của bạn!</string>
|
<string name="settings_dark_mode_message">Chọn chế độ phù hợp nhất với phong cách của bạn!</string>
|
||||||
@@ -159,13 +161,15 @@
|
|||||||
<string name="superuser_notification">Thông báo của Superuser</string>
|
<string name="superuser_notification">Thông báo của Superuser</string>
|
||||||
<string name="settings_su_reauth_title">Xác thực lại sau khi nâng cấp</string>
|
<string name="settings_su_reauth_title">Xác thực lại sau khi nâng cấp</string>
|
||||||
<string name="settings_su_reauth_summary">Xác thực lại quyền Superuser sau khi nâng cấp ứng dụng</string>
|
<string name="settings_su_reauth_summary">Xác thực lại quyền Superuser sau khi nâng cấp ứng dụng</string>
|
||||||
<string name="settings_su_tapjack_title">Kích hoạt tính năng bảo vệ bằng Tapjacking</string>
|
<string name="settings_su_tapjack_title">Bảo vệ khỏi Tapjacking</string>
|
||||||
<string name="settings_su_tapjack_summary">Hộp thoại nhắc Superuser sẽ không trả lời đầu vào khi bị che khuất bởi bất kỳ cửa sổ hoặc lớp phủ nào khác</string>
|
<string name="settings_su_tapjack_summary">Hộp thoại nhắc Superuser sẽ không trả lời đầu vào khi bị che khuất bởi bất kỳ cửa sổ hoặc lớp phủ nào khác</string>
|
||||||
<string name="settings_su_biometric_title">Bật xác thực sinh trắc học</string>
|
<string name="settings_su_biometric_title">Bật xác thực sinh trắc học</string>
|
||||||
<string name="settings_su_biometric_summary">Sử dụng xác thực sinh trắc học để xác nhận yêu cầu Superuser</string>
|
<string name="settings_su_biometric_summary">Sử dụng xác thực sinh trắc học để xác nhận yêu cầu Superuser</string>
|
||||||
<string name="no_biometric">Thiết bị của bạn không được hỗ trợ hoặc không có phương pháp xác thực sinh trắc học nào được kích hoạt</string>
|
<string name="no_biometric">Thiết bị của bạn không được hỗ trợ hoặc không có phương pháp xác thực sinh trắc học nào được kích hoạt</string>
|
||||||
<string name="settings_customization">Tùy biến</string>
|
<string name="settings_customization">Tùy biến</string>
|
||||||
<string name="setting_add_shortcut_summary">Thêm một phím tắt đẹp vào màn hình trong trường hợp khó nhận ra tên và biểu tượng sau khi ẩn ứng dụng</string>
|
<string name="setting_add_shortcut_summary">Thêm một phím tắt đẹp vào màn hình trong trường hợp khó nhận ra tên và biểu tượng sau khi ẩn ứng dụng</string>
|
||||||
|
<string name="settings_doh_title">DNS over HTTPS</string>
|
||||||
|
<string name="settings_doh_description">Workaround DNS poisoning in some nations</string>
|
||||||
|
|
||||||
<string name="multiuser_mode">Chế độ đa người dùng</string>
|
<string name="multiuser_mode">Chế độ đa người dùng</string>
|
||||||
<string name="settings_owner_only">Chỉ chủ sở hữu thiết bị</string>
|
<string name="settings_owner_only">Chỉ chủ sở hữu thiết bị</string>
|
||||||
@@ -186,9 +190,12 @@
|
|||||||
<!--Thông báo-->
|
<!--Thông báo-->
|
||||||
<string name="update_channel">Cập nhật Magisk</string>
|
<string name="update_channel">Cập nhật Magisk</string>
|
||||||
<string name="progress_channel">Thông báo tiến độ</string>
|
<string name="progress_channel">Thông báo tiến độ</string>
|
||||||
|
<string name="updated_channel">Cập nhật hoàn tất</string>
|
||||||
<string name="download_complete">Tải về hoàn tất</string>
|
<string name="download_complete">Tải về hoàn tất</string>
|
||||||
<string name="download_file_error">Lỗi khi tải xuống tệp</string>
|
<string name="download_file_error">Lỗi khi tải xuống tệp</string>
|
||||||
<string name="magisk_update_title">Cập nhật Magisk có sẵn!</string>
|
<string name="magisk_update_title">Cập nhật Magisk có sẵn!</string>
|
||||||
|
<string name="updated_title">Đã cập nhật Magisk</string>
|
||||||
|
<string name="updated_text">Chạm để mở ứng dụng</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="yes">Đồng ý</string>
|
<string name="yes">Đồng ý</string>
|
||||||
@@ -203,10 +210,10 @@
|
|||||||
<string name="hide_app_title">Đang ẩn ứng dụng Magisk…</string>
|
<string name="hide_app_title">Đang ẩn ứng dụng Magisk…</string>
|
||||||
<string name="open_link_failed_toast">Không tìm thấy ứng dụng nào để mở liên kết</string>
|
<string name="open_link_failed_toast">Không tìm thấy ứng dụng nào để mở liên kết</string>
|
||||||
<string name="complete_uninstall">Hoàn thành Gỡ cài đặt</string>
|
<string name="complete_uninstall">Hoàn thành Gỡ cài đặt</string>
|
||||||
<string name="restore_img">Khôi phục đĩa ảnh boot</string>
|
<string name="restore_img">Khôi phục đĩa ảnh boot (image)</string>
|
||||||
<string name="restore_img_msg">Đang khôi phục…</string>
|
<string name="restore_img_msg">Đang khôi phục…</string>
|
||||||
<string name="restore_done">Đã khôi phục xong!</string>
|
<string name="restore_done">Đã khôi phục xong!</string>
|
||||||
<string name="restore_fail">Bản gốc không tồn tại!</string>
|
<string name="restore_fail">Bản sao lưu gốc không tồn tại!</string>
|
||||||
<string name="setup_fail">Thiết lập không thành công</string>
|
<string name="setup_fail">Thiết lập không thành công</string>
|
||||||
<string name="env_fix_title">Yêu cầu thiết lập bổ sung</string>
|
<string name="env_fix_title">Yêu cầu thiết lập bổ sung</string>
|
||||||
<string name="env_fix_msg">Thiết bị của bạn cần thiết lập bổ sung để Magisk hoạt động bình thường. Bạn có muốn tiếp tục và khởi động lại không?</string>
|
<string name="env_fix_msg">Thiết bị của bạn cần thiết lập bổ sung để Magisk hoạt động bình thường. Bạn có muốn tiếp tục và khởi động lại không?</string>
|
||||||
@@ -216,14 +223,16 @@
|
|||||||
<string name="unsupport_magisk_msg">Phiên bản hiện tại của ứng dụng không hỗ trợ phiên bản Magisk thấp hơn %1$s.\n\nỨng dụng sẽ hoạt động như thể Magisk chưa được cài đặt. Vui lòng nâng cấp lên phiên bản mới nhất.</string>
|
<string name="unsupport_magisk_msg">Phiên bản hiện tại của ứng dụng không hỗ trợ phiên bản Magisk thấp hơn %1$s.\n\nỨng dụng sẽ hoạt động như thể Magisk chưa được cài đặt. Vui lòng nâng cấp lên phiên bản mới nhất.</string>
|
||||||
<string name="unsupport_general_title">Trạng thái bất thường</string>
|
<string name="unsupport_general_title">Trạng thái bất thường</string>
|
||||||
<string name="unsupport_system_app_msg">Không hỗ trợ chạy ứng dụng này dưới dạng ứng dụng hệ thống. Vui lòng hoàn nguyên ứng dụng về ứng dụng người dùng.</string>
|
<string name="unsupport_system_app_msg">Không hỗ trợ chạy ứng dụng này dưới dạng ứng dụng hệ thống. Vui lòng hoàn nguyên ứng dụng về ứng dụng người dùng.</string>
|
||||||
<string name="unsupport_other_su_msg">Một lệnh \"su\" không thuộc về Magisk được phát hiện. Vui lòng loại bỏ su không được hỗ trợ khác.</string>
|
<string name="unsupport_other_su_msg">Một lệnh \"su\" không thuộc về Magisk được phát hiện. Vui lòng gỡ bỏ bất kì phương pháp root khác và/hoặc cài đặt lại Magisk.</string>
|
||||||
<string name="unsupport_external_storage_msg">Magisk được cài đặt vào bộ nhớ ngoài. Vui lòng chuyển ứng dụng vào bộ nhớ trong.</string>
|
<string name="unsupport_external_storage_msg">Magisk được cài đặt vào bộ nhớ ngoài. Vui lòng chuyển ứng dụng vào bộ nhớ trong.</string>
|
||||||
<string name="unsupport_nonroot_stub_msg">Ứng dụng không thể tiếp tục hoạt động ở trạng thái ẩn vì mất quyền root. Vui lòng khôi phục nó trở lại APK ban đầu.</string>
|
<string name="unsupport_nonroot_stub_msg">Ứng dụng không thể tiếp tục hoạt động ở trạng thái ẩn vì mất quyền root. Vui lòng khôi phục nó trở lại APK ban đầu.</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Cấp quyền lưu trữ để bật chức năng này</string>
|
<string name="external_rw_permission_denied">Cấp quyền lưu trữ để bật chức năng này</string>
|
||||||
|
<string name="install_unknown_denied">Cho phép "cài đặt ứng dụng không rõ nguồn gốc" để bật chức năng này</string>
|
||||||
<string name="add_shortcut_title">Thêm lối tắt vào màn hình chính</string>
|
<string name="add_shortcut_title">Thêm lối tắt vào màn hình chính</string>
|
||||||
<string name="add_shortcut_msg">Sau khi ẩn ứng dụng này, tên và biểu tượng của nó có thể trở nên khó nhận ra. Bạn có muốn thêm một phím tắt đẹp vào màn hình chính không?</string>
|
<string name="add_shortcut_msg">Sau khi ẩn ứng dụng này, tên và biểu tượng của nó có thể trở nên khó nhận ra. Bạn có muốn thêm một phím tắt đẹp vào màn hình chính không?</string>
|
||||||
<string name="app_not_found">Không tìm thấy ứng dụng nào để xử lý hành động này</string>
|
<string name="app_not_found">Không tìm thấy ứng dụng nào để xử lý hành động này</string>
|
||||||
<string name="reboot_apply_change">Khởi động lại để áp dụng các thay đổi</string>
|
<string name="reboot_apply_change">Khởi động lại để áp dụng các thay đổi</string>
|
||||||
|
<string name="restore_app_confirmation">Điều này sẽ khôi phục ứng dụng ẩn về nguyên bản. Bạn chắc chắn muốn làm điều này chứ?</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
<string name="home_notice_content">仅从官方 GitHub 页面下载 Magisk。未知来源的文件可能具有恶意行为!</string>
|
<string name="home_notice_content">仅从官方 GitHub 页面下载 Magisk。未知来源的文件可能具有恶意行为!</string>
|
||||||
<string name="home_support_title">支持开发</string>
|
<string name="home_support_title">支持开发</string>
|
||||||
<string name="home_item_source">源代码</string>
|
<string name="home_item_source">源代码</string>
|
||||||
<string name="home_support_content">Magisk 将一直保持自由且开源,向开发者捐赠以表示支持。</string>
|
<string name="home_support_content">Magisk 将一直保持免费且开源,向开发者捐赠以表示支持。</string>
|
||||||
<string name="home_installed_version">当前</string>
|
<string name="home_installed_version">当前</string>
|
||||||
<string name="home_latest_version">最新</string>
|
<string name="home_latest_version">最新</string>
|
||||||
<string name="invalid_update_channel">无效的更新通道</string>
|
<string name="invalid_update_channel">无效的更新通道</string>
|
||||||
@@ -190,9 +190,12 @@
|
|||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="update_channel">更新提示</string>
|
<string name="update_channel">更新提示</string>
|
||||||
<string name="progress_channel">下载进度</string>
|
<string name="progress_channel">下载进度</string>
|
||||||
|
<string name="updated_channel">更新完成</string>
|
||||||
<string name="download_complete">下载完成</string>
|
<string name="download_complete">下载完成</string>
|
||||||
<string name="download_file_error">下载失败</string>
|
<string name="download_file_error">下载失败</string>
|
||||||
<string name="magisk_update_title">Magisk 已发布新版本!</string>
|
<string name="magisk_update_title">Magisk 已发布新版本!</string>
|
||||||
|
<string name="updated_title">Magisk 已完成更新</string>
|
||||||
|
<string name="updated_text">点按即可打开应用</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="yes">是</string>
|
<string name="yes">是</string>
|
||||||
@@ -225,6 +228,7 @@
|
|||||||
<string name="unsupport_nonroot_stub_msg">超级用户权限丢失,应用无法在隐藏状态下继续工作,请恢复到原始 Magisk 应用。</string>
|
<string name="unsupport_nonroot_stub_msg">超级用户权限丢失,应用无法在隐藏状态下继续工作,请恢复到原始 Magisk 应用。</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">允许访问存储空间以使用此功能</string>
|
<string name="external_rw_permission_denied">允许访问存储空间以使用此功能</string>
|
||||||
|
<string name="install_unknown_denied">允许安装未知应用以使用此功能</string>
|
||||||
<string name="add_shortcut_title">添加快捷方式到桌面</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="app_not_found">找不到可处理此操作的应用</string>
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
<!--Install-->
|
<!--Install-->
|
||||||
<string name="keep_force_encryption">保持強制加密</string>
|
<string name="keep_force_encryption">保持強制加密</string>
|
||||||
<string name="keep_dm_verity">保持 AVB 2.0/dm-verity</string>
|
<string name="keep_dm_verity">保持 AVB 2.0/dm-verity</string>
|
||||||
|
<string name="patch_vbmeta">修補 vbmeta 到開機映像檔</string>
|
||||||
<string name="recovery_mode">安裝至 Recovery</string>
|
<string name="recovery_mode">安裝至 Recovery</string>
|
||||||
<string name="install_options_title">選項</string>
|
<string name="install_options_title">選項</string>
|
||||||
<string name="install_method_title">安裝方式</string>
|
<string name="install_method_title">安裝方式</string>
|
||||||
@@ -107,6 +108,9 @@
|
|||||||
<string name="module_state_restore">還原</string>
|
<string name="module_state_restore">還原</string>
|
||||||
<string name="module_action_install_external">從本機安裝</string>
|
<string name="module_action_install_external">從本機安裝</string>
|
||||||
<string name="update_available">有可用的更新</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 模組因未相容而不載入</string>
|
||||||
|
|
||||||
<!--Settings -->
|
<!--Settings -->
|
||||||
<string name="settings_dark_mode_title">主題模式</string>
|
<string name="settings_dark_mode_title">主題模式</string>
|
||||||
@@ -224,6 +228,7 @@
|
|||||||
<string name="add_shortcut_title">新增捷徑到首頁</string>
|
<string name="add_shortcut_title">新增捷徑到首頁</string>
|
||||||
<string name="add_shortcut_msg">在隱藏 Magisk 之後,其名稱與圖示將難以辨識。您想要新增一個精緻的捷徑到桌面嗎?</string>
|
<string name="add_shortcut_msg">在隱藏 Magisk 之後,其名稱與圖示將難以辨識。您想要新增一個精緻的捷徑到桌面嗎?</string>
|
||||||
<string name="app_not_found">沒有可以處理這個操作的應用程式</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>
|
</resources>
|
||||||
|
@@ -131,8 +131,8 @@
|
|||||||
<string name="settings_update_channel_title">Update Channel</string>
|
<string name="settings_update_channel_title">Update Channel</string>
|
||||||
<string name="settings_update_stable">Stable</string>
|
<string name="settings_update_stable">Stable</string>
|
||||||
<string name="settings_update_beta">Beta</string>
|
<string name="settings_update_beta">Beta</string>
|
||||||
<string name="settings_update_custom">Custom Channel</string>
|
<string name="settings_update_custom">Custom</string>
|
||||||
<string name="settings_update_custom_msg">Insert a custom URL</string>
|
<string name="settings_update_custom_msg">Insert a custom channel URL</string>
|
||||||
<string name="settings_zygisk_summary">Run parts of Magisk in the zygote daemon</string>
|
<string name="settings_zygisk_summary">Run parts of Magisk in the zygote daemon</string>
|
||||||
<string name="settings_denylist_title">Enforce DenyList</string>
|
<string name="settings_denylist_title">Enforce DenyList</string>
|
||||||
<string name="settings_denylist_summary">Processes on the denylist will have all Magisk modifications reverted</string>
|
<string name="settings_denylist_summary">Processes on the denylist will have all Magisk modifications reverted</string>
|
||||||
@@ -190,9 +190,12 @@
|
|||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="update_channel">Magisk Updates</string>
|
<string name="update_channel">Magisk Updates</string>
|
||||||
<string name="progress_channel">Progress Notifications</string>
|
<string name="progress_channel">Progress Notifications</string>
|
||||||
|
<string name="updated_channel">Update Complete</string>
|
||||||
<string name="download_complete">Download complete</string>
|
<string name="download_complete">Download complete</string>
|
||||||
<string name="download_file_error">Error downloading file</string>
|
<string name="download_file_error">Error downloading file</string>
|
||||||
<string name="magisk_update_title">Magisk Update Available!</string>
|
<string name="magisk_update_title">Magisk Update Available!</string>
|
||||||
|
<string name="updated_title">Magisk Updated</string>
|
||||||
|
<string name="updated_text">Tap to open app</string>
|
||||||
|
|
||||||
<!--Toasts, Dialogs-->
|
<!--Toasts, Dialogs-->
|
||||||
<string name="yes">Yes</string>
|
<string name="yes">Yes</string>
|
||||||
@@ -225,6 +228,7 @@
|
|||||||
<string name="unsupport_nonroot_stub_msg">The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK.</string>
|
<string name="unsupport_nonroot_stub_msg">The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK.</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Grant storage permission to enable this functionality</string>
|
<string name="external_rw_permission_denied">Grant storage permission to enable this functionality</string>
|
||||||
|
<string name="install_unknown_denied">Allow "install unknown apps" to enable this functionality</string>
|
||||||
<string name="add_shortcut_title">Add shortcut to home screen</string>
|
<string name="add_shortcut_title">Add shortcut to home screen</string>
|
||||||
<string name="add_shortcut_msg">After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen?</string>
|
<string name="add_shortcut_msg">After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen?</string>
|
||||||
<string name="app_not_found">No app found to handle this action</string>
|
<string name="app_not_found">No app found to handle this action</string>
|
||||||
|
6
build.py
6
build.py
@@ -405,7 +405,7 @@ def cleanup(args):
|
|||||||
def setup_ndk(args):
|
def setup_ndk(args):
|
||||||
os_name = platform.system().lower()
|
os_name = platform.system().lower()
|
||||||
ndk_ver = config['ndkVersion']
|
ndk_ver = config['ndkVersion']
|
||||||
url = f'https://dl.google.com/android/repository/android-ndk-r{ndk_ver}-{os_name}-x86_64.zip'
|
url = f'https://dl.google.com/android/repository/android-ndk-r{ndk_ver}-{os_name}.zip'
|
||||||
ndk_zip = url.split('/')[-1]
|
ndk_zip = url.split('/')[-1]
|
||||||
|
|
||||||
header(f'* Downloading {ndk_zip}')
|
header(f'* Downloading {ndk_zip}')
|
||||||
@@ -417,7 +417,7 @@ def setup_ndk(args):
|
|||||||
with zipfile.ZipFile(ndk_zip, 'r') as zf:
|
with zipfile.ZipFile(ndk_zip, 'r') as zf:
|
||||||
for info in zf.infolist():
|
for info in zf.infolist():
|
||||||
vprint(f'Extracting {info.filename}')
|
vprint(f'Extracting {info.filename}')
|
||||||
if info.external_attr == 2716663808: # symlink
|
if info.external_attr >> 28 == 0xA: # symlink
|
||||||
src = zf.read(info).decode("utf-8")
|
src = zf.read(info).decode("utf-8")
|
||||||
dest = op.join(ndk_root, info.filename)
|
dest = op.join(ndk_root, info.filename)
|
||||||
os.symlink(src, dest)
|
os.symlink(src, dest)
|
||||||
@@ -491,7 +491,7 @@ def patch_avd_ramdisk(args):
|
|||||||
'scripts/avd_patch.sh', '/data/local/tmp'])
|
'scripts/avd_patch.sh', '/data/local/tmp'])
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
error('adb push failed!')
|
error('adb push failed!')
|
||||||
proc = execv([adb_path, 'push', backup, '/data/local/tmp/ramdisk.cpio.gz'])
|
proc = execv([adb_path, 'push', backup, '/data/local/tmp/ramdisk.cpio.tmp'])
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
error('adb push failed!')
|
error('adb push failed!')
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`kotlin-dsl`
|
`kotlin-dsl`
|
||||||
}
|
}
|
||||||
@@ -15,10 +17,16 @@ gradlePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile> {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("gradle-plugin", "1.6.10"))
|
implementation(kotlin("gradle-plugin", "1.6.10"))
|
||||||
implementation("com.android.tools.build:gradle:7.0.4")
|
implementation("com.android.tools.build:gradle:7.1.2")
|
||||||
implementation("androidx.navigation:navigation-safe-args-gradle-plugin:2.4.0-rc01")
|
implementation("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-alpha01")
|
||||||
implementation("io.michaelrocks:paranoid-gradle-plugin:0.3.7")
|
implementation("io.michaelrocks:paranoid-gradle-plugin:0.3.7")
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit:5.12.0.202106070339-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit:5.12.0.202106070339-r")
|
||||||
}
|
}
|
||||||
|
@@ -74,36 +74,13 @@ fun genKeyData(keysDir: File, outSrc: File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun genStubManifest(srcDir: File, outDir: File): String {
|
fun genStubManifest(srcDir: File, outDir: File): String {
|
||||||
class Component(
|
|
||||||
val real: String,
|
|
||||||
val stub: String,
|
|
||||||
val xml: String
|
|
||||||
)
|
|
||||||
|
|
||||||
outDir.deleteRecursively()
|
outDir.deleteRecursively()
|
||||||
|
|
||||||
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
|
||||||
mainPkgDir.mkdirs()
|
|
||||||
|
|
||||||
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
||||||
|
|
||||||
val cmpList = mutableListOf<Component>()
|
val cmpList = mutableListOf<String>()
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"androidx.core.app.CoreComponentFactory",
|
|
||||||
"DelegateComponentFactory",
|
|
||||||
""
|
|
||||||
))
|
|
||||||
|
|
||||||
cmpList.add(Component(
|
|
||||||
"com.topjohnwu.magisk.core.App",
|
|
||||||
"DelegateApplication",
|
|
||||||
""
|
|
||||||
))
|
|
||||||
|
|
||||||
cmpList.add(Component(
|
|
||||||
"com.topjohnwu.magisk.core.Provider",
|
|
||||||
"dummy.DummyProvider",
|
|
||||||
"""
|
"""
|
||||||
|<provider
|
|<provider
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@@ -111,11 +88,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| android:directBootAware="true"
|
| android:directBootAware="true"
|
||||||
| android:exported="false"
|
| android:exported="false"
|
||||||
| android:grantUriPermissions="true" />""".ind(2)
|
| android:grantUriPermissions="true" />""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.core.Receiver",
|
|
||||||
"dummy.DummyReceiver",
|
|
||||||
"""
|
"""
|
||||||
|<receiver
|
|<receiver
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@@ -124,6 +99,7 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| <intent-filter>
|
| <intent-filter>
|
||||||
| <action android:name="android.intent.action.LOCALE_CHANGED" />
|
| <action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
| <action android:name="android.intent.action.UID_REMOVED" />
|
| <action android:name="android.intent.action.UID_REMOVED" />
|
||||||
|
| <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
| <intent-filter>
|
| <intent-filter>
|
||||||
| <action android:name="android.intent.action.PACKAGE_REPLACED" />
|
| <action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
@@ -132,11 +108,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| <data android:scheme="package" />
|
| <data android:scheme="package" />
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
|</receiver>""".ind(2)
|
|</receiver>""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.ui.MainActivity",
|
|
||||||
"DownloadActivity",
|
|
||||||
"""
|
"""
|
||||||
|<activity
|
|<activity
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@@ -146,43 +120,38 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| <category android:name="android.intent.category.LAUNCHER" />
|
| <category android:name="android.intent.category.LAUNCHER" />
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
|</activity>""".ind(2)
|
|</activity>""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.ui.surequest.SuRequestActivity",
|
|
||||||
"",
|
|
||||||
"""
|
"""
|
||||||
|<activity
|
|<activity
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
| android:directBootAware="true"
|
| android:directBootAware="true"
|
||||||
| android:excludeFromRecents="true"
|
| android:excludeFromRecents="true"
|
||||||
| android:exported="false"
|
| android:exported="false"
|
||||||
|
| android:taskAffinity=""
|
||||||
| tools:ignore="AppLinkUrlError">
|
| tools:ignore="AppLinkUrlError">
|
||||||
| <intent-filter>
|
| <intent-filter>
|
||||||
| <action android:name="android.intent.action.VIEW"/>
|
| <action android:name="android.intent.action.VIEW"/>
|
||||||
| <category android:name="android.intent.category.DEFAULT"/>
|
| <category android:name="android.intent.category.DEFAULT"/>
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
|</activity>""".ind(2)
|
|</activity>""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.core.download.DownloadService",
|
|
||||||
"",
|
|
||||||
"""
|
"""
|
||||||
|<service
|
|<service
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
| android:exported="false" />""".trimIndent().ind(2)
|
| android:exported="false" />""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.core.JobService",
|
|
||||||
"",
|
|
||||||
"""
|
"""
|
||||||
|<service
|
|<service
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
| android:exported="false"
|
| android:exported="false"
|
||||||
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
|
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
val names = mutableListOf<String>()
|
val names = mutableListOf<String>()
|
||||||
names.addAll(c1)
|
names.addAll(c1)
|
||||||
@@ -190,77 +159,66 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
names.addAll(c3.subList(0, 10))
|
names.addAll(c3.subList(0, 10))
|
||||||
names.shuffle(RANDOM)
|
names.shuffle(RANDOM)
|
||||||
|
|
||||||
// Distinct by lower case to support case insensitive file systems
|
val pkgNames = names
|
||||||
val pkgNames = names.distinctBy { it.toLowerCase(Locale.ROOT) }
|
// Distinct by lower case to support case insensitive file systems
|
||||||
|
.distinctBy { it.toLowerCase(Locale.ROOT) }
|
||||||
|
// Old Android does not support capitalized package names
|
||||||
|
// Check Android 7.0.0 PackageParser#buildClassName
|
||||||
|
.map { it.decapitalize(Locale.ROOT) }
|
||||||
|
|
||||||
var idx = 0
|
|
||||||
fun isJavaKeyword(name: String) = when (name) {
|
fun isJavaKeyword(name: String) = when (name) {
|
||||||
"do", "if", "for", "int", "new", "try" -> true
|
"do", "if", "for", "int", "new", "try" -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genCmpName() : String {
|
val cmps = mutableListOf<String>()
|
||||||
var pkgName : String
|
val usedNames = mutableListOf<String>()
|
||||||
|
|
||||||
|
fun genCmpName(): String {
|
||||||
|
var pkgName: String
|
||||||
do {
|
do {
|
||||||
pkgName = pkgNames[idx++]
|
pkgName = pkgNames.random(kRANDOM)
|
||||||
} while (isJavaKeyword(pkgName))
|
} while (isJavaKeyword(pkgName))
|
||||||
|
|
||||||
var clzName : String
|
var clzName: String
|
||||||
do {
|
do {
|
||||||
clzName = names.random(kRANDOM)
|
clzName = names.random(kRANDOM)
|
||||||
} while (isJavaKeyword(clzName))
|
} while (isJavaKeyword(clzName))
|
||||||
return "${pkgName}.${clzName}"
|
val cmp = "${pkgName}.${clzName}"
|
||||||
|
usedNames.add(cmp)
|
||||||
|
return cmp
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genClass(clzName: String, type: String) {
|
fun genClass(type: String) {
|
||||||
|
val clzName = genCmpName()
|
||||||
val (pkg, name) = clzName.split('.')
|
val (pkg, name) = clzName.split('.')
|
||||||
val pkgDir = File(outDir, pkg)
|
val pkgDir = File(outDir, pkg)
|
||||||
pkgDir.mkdir()
|
pkgDir.mkdirs()
|
||||||
PrintStream(File(pkgDir, "$name.java")).use {
|
PrintStream(File(pkgDir, "$name.java")).use {
|
||||||
it.println("package $pkg;")
|
it.println("package $pkg;")
|
||||||
it.println("public class $name extends com.topjohnwu.magisk.$type {}")
|
it.println("public class $name extends com.topjohnwu.magisk.$type {}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cmps = mutableListOf<String>()
|
// Generate 2 non redirect-able classes
|
||||||
val usedNames = mutableListOf<String>()
|
genClass("DelegateComponentFactory")
|
||||||
val maps = StringBuilder()
|
genClass("DelegateApplication")
|
||||||
|
|
||||||
for (gen in cmpList) {
|
for (gen in cmpList) {
|
||||||
val name = genCmpName()
|
val name = genCmpName()
|
||||||
usedNames.add(name)
|
cmps.add(gen.format(name))
|
||||||
maps.append("|map.put(\"$name\", \"${gen.real}\");".ind(2))
|
|
||||||
maps.append('\n')
|
|
||||||
if (gen.stub.isNotEmpty()) {
|
|
||||||
if (gen.stub != "DelegateComponentFactory") {
|
|
||||||
maps.append("|internalMap.put(\"$name\", com.topjohnwu.magisk.${gen.stub}.class);".ind(2))
|
|
||||||
maps.append('\n')
|
|
||||||
}
|
|
||||||
if (gen.stub.startsWith("Delegate")) {
|
|
||||||
genClass(name, gen.stub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gen.xml.isNotEmpty()) {
|
|
||||||
cmps.add(gen.xml.format(name))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shuffle the order of the components
|
// Shuffle the order of the components
|
||||||
cmps.shuffle(RANDOM)
|
cmps.shuffle(RANDOM)
|
||||||
|
|
||||||
val xml = File(srcDir, "AndroidManifest.xml").readText()
|
val xml = File(srcDir, "AndroidManifest.xml").readText()
|
||||||
val genXml = xml.format(usedNames[0], usedNames[1], cmps.joinToString("\n\n"))
|
return xml.format(usedNames[0], usedNames[1], cmps.joinToString("\n\n"))
|
||||||
|
|
||||||
// Write mapping information to code
|
|
||||||
val mapping = File(srcDir, "Mapping.java").readText().format(maps)
|
|
||||||
PrintStream(File(mainPkgDir, "Mapping.java")).use {
|
|
||||||
it.print(mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
return genXml
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genEncryptedResources(res: InputStream, outDir: File) {
|
fun genEncryptedResources(res: InputStream, outDir: File) {
|
||||||
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
||||||
|
mainPkgDir.mkdirs()
|
||||||
|
|
||||||
// Generate iv and key
|
// Generate iv and key
|
||||||
val iv = ByteArray(16)
|
val iv = ByteArray(16)
|
||||||
|
@@ -29,7 +29,7 @@ fun Project.setupCommon() {
|
|||||||
androidBase {
|
androidBase {
|
||||||
compileSdkVersion(31)
|
compileSdkVersion(31)
|
||||||
buildToolsVersion = "31.0.0"
|
buildToolsVersion = "31.0.0"
|
||||||
ndkPath = "${System.getenv("ANDROID_SDK_ROOT")}/ndk/magisk"
|
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
@@ -123,7 +123,8 @@ fun Project.setupApp() {
|
|||||||
inputs.property("versionCode", Config.versionCode)
|
inputs.property("versionCode", Config.versionCode)
|
||||||
into("src/main/assets")
|
into("src/main/assets")
|
||||||
from(rootProject.file("scripts")) {
|
from(rootProject.file("scripts")) {
|
||||||
include("util_functions.sh", "boot_patch.sh", "uninstaller.sh", "addon.d.sh")
|
include("util_functions.sh", "boot_patch.sh", "addon.d.sh")
|
||||||
|
include("uninstaller.sh", "module_installer.sh")
|
||||||
}
|
}
|
||||||
from(rootProject.file("tools/bootctl"))
|
from(rootProject.file("tools/bootctl"))
|
||||||
into("chromeos") {
|
into("chromeos") {
|
||||||
@@ -187,8 +188,6 @@ fun Project.setupStub() {
|
|||||||
val apkTmp = File("${apk}.tmp")
|
val apkTmp = File("${apk}.tmp")
|
||||||
|
|
||||||
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedManifest") {
|
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedManifest") {
|
||||||
inputs.property("versionCode", Config.versionCode)
|
|
||||||
outputs.file(manifest)
|
|
||||||
doLast {
|
doLast {
|
||||||
val xml = genStubManifest(templateDir, outSrcDir)
|
val xml = genStubManifest(templateDir, outSrcDir)
|
||||||
manifest.parentFile.mkdirs()
|
manifest.parentFile.mkdirs()
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
# Magisk Changelog
|
# Magisk Changelog
|
||||||
|
|
||||||
|
### v24.1
|
||||||
|
|
||||||
|
- [App] Stability improvements
|
||||||
|
|
||||||
### v24.0
|
### v24.0
|
||||||
|
|
||||||
- [General] MagiskHide is removed from Magisk
|
- [General] MagiskHide is removed from Magisk
|
||||||
- [General] Support 64-bit only systems
|
|
||||||
- [General] Support Android 12
|
- [General] Support Android 12
|
||||||
|
- [General] Support devices that do not support 32-bit and only runs 64-bit code
|
||||||
- [General] Update BusyBox to 1.34.1
|
- [General] Update BusyBox to 1.34.1
|
||||||
- [Zygisk] Introduce new feature: Zygisk
|
- [Zygisk] Introduce new feature: Zygisk
|
||||||
- [Zygisk] Introduce DenyList feature to revert Magisk features in user selected processes
|
- [Zygisk] Introduce DenyList feature to revert Magisk features in user selected processes
|
||||||
|
@@ -34,11 +34,11 @@ Next, we need to know whether your device has a separate `vbmeta` partition.
|
|||||||
- If you find `vbmeta`, `vbmeta_a`, or `vbmeta_b`, then yes, your device **has** a separate `vbmeta` partition
|
- If you find `vbmeta`, `vbmeta_a`, or `vbmeta_b`, then yes, your device **has** a separate `vbmeta` partition
|
||||||
- Otherwise, your device **does not** have a separate `vbmeta` partition.
|
- Otherwise, your device **does not** have a separate `vbmeta` partition.
|
||||||
|
|
||||||
Quick recap, at this point, you should know and prepared:
|
Quick recap, at this point, you should have known and prepared:
|
||||||
|
|
||||||
1. Whether your device has boot ramdisk
|
1. Whether your device has boot ramdisk
|
||||||
2. Whether your device has a separate `vbmeta` partition
|
2. Whether your device has a separate `vbmeta` partition
|
||||||
3. A `boot.img` or `recovery.img` based on the result of (1)
|
3. A `boot.img` or `recovery.img` based on (1)
|
||||||
|
|
||||||
Let's continue to [Patching Images](#patching-images).
|
Let's continue to [Patching Images](#patching-images).
|
||||||
|
|
||||||
@@ -103,16 +103,15 @@ Unlocking the bootloader on modern Samsung devices have some caveats. The newly
|
|||||||
- Use either [samfirm.js](https://github.com/jesec/samfirm.js), [Frija](https://forum.xda-developers.com/s10-plus/how-to/tool-frija-samsung-firmware-downloader-t3910594), or [Samloader](https://forum.xda-developers.com/s10-plus/how-to/tool-samloader-samfirm-frija-replacement-t4105929) to download the latest firmware zip of your device directly from Samsung servers.
|
- Use either [samfirm.js](https://github.com/jesec/samfirm.js), [Frija](https://forum.xda-developers.com/s10-plus/how-to/tool-frija-samsung-firmware-downloader-t3910594), or [Samloader](https://forum.xda-developers.com/s10-plus/how-to/tool-samloader-samfirm-frija-replacement-t4105929) to download the latest firmware zip of your device directly from Samsung servers.
|
||||||
- Unzip the firmware and copy the `AP` tar file to your device. It is normally named as `AP_[device_model_sw_ver].tar.md5`
|
- Unzip the firmware and copy the `AP` tar file to your device. It is normally named as `AP_[device_model_sw_ver].tar.md5`
|
||||||
- Press the **Install** button in the Magisk card
|
- Press the **Install** button in the Magisk card
|
||||||
- If you are patching a recovery image, check the **"Recovery Mode"** option
|
- If your device does **NOT** have boot ramdisk, check the **"Recovery Mode"** option
|
||||||
- Choose **"Select and Patch a File"** in method, and select the `AP` tar file
|
- Choose **"Select and Patch a File"** in method, and select the `AP` tar file
|
||||||
- The Magisk app will patch the whole firmware file to `[Internal Storage]/Download/magisk_patched_[random_strings].tar`
|
|
||||||
- Start the installation, and copy the patched tar file to your PC using ADB:<br>
|
- Start the installation, and copy the patched tar file to your PC using ADB:<br>
|
||||||
`adb pull /sdcard/Download/magisk_patched_[random_strings].tar`<br>
|
`adb pull /sdcard/Download/magisk_patched_[random_strings].tar`<br>
|
||||||
**DO NOT USE MTP** as it is known to corrupt large files.
|
**DO NOT USE MTP** as it is known to corrupt large files.
|
||||||
- Reboot to download mode. Open Odin on your PC, and flash `magisk_patched.tar` as `AP`, together with `BL`, `CP`, and `CSC` (**NOT** `HOME_CSC` because we want to **wipe data**) from the original firmware.
|
- Reboot to download mode. Open Odin on your PC, and flash `magisk_patched.tar` as `AP`, together with `BL`, `CP`, and `CSC` (**NOT** `HOME_CSC` because we want to **wipe data**) from the original firmware.
|
||||||
- Your device should reboot automatically once Odin finished flashing. Agree to do a factory reset if asked.
|
- Your device should reboot automatically once Odin finished flashing. Agree to do a factory reset if asked.
|
||||||
- If your device does **NOT** have boot ramdisk, reboot to recovery now to enable Magisk (reason stated in [Magisk in Recovery](#magisk-in-recovery)).
|
- If your device does **NOT** have boot ramdisk, reboot to recovery now to enable Magisk (reason stated in [Magisk in Recovery](#magisk-in-recovery)).
|
||||||
- Install the Magisk app you've downloaded and launch the app. It should show a dialog asking for additional setup.
|
- Install the Magisk app you've already downloaded and launch the app. It should show a dialog asking for additional setup.
|
||||||
- Let the app do its job and automatically reboot the device. Voila!
|
- Let the app do its job and automatically reboot the device. Voila!
|
||||||
|
|
||||||
### Upgrading the OS
|
### Upgrading the OS
|
||||||
|
23
docs/releases/24100.md
Normal file
23
docs/releases/24100.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
## 2022.1.28 Magisk v24.1
|
||||||
|
|
||||||
|
> For those coming from v24.0, v24.1 only has some minor app improvements. The following are copied from v24.0 release notes.
|
||||||
|
|
||||||
|
It has been a while since the last public release, long time no see! A personal update for those unaware: I am now working at Google on the Android Platform Security team. Without further ado, let's jump right into it!
|
||||||
|
|
||||||
|
### MagiskHide Removal
|
||||||
|
|
||||||
|
I have lost interest in fighting this battle for quite a while; plus, the existing MagiskHide implementation is flawed in so many ways. Decoupling Magisk from root hiding is, in my opinion, beneficial to the community. Ever since my announcement on Twitter months ago, highly effective "root hiding" modules (much **MUCH** better than MagiskHide) has been flourishing, which again shows that people are way more capable than I am on this subject. So why not give those determined their time to shine, and let me focus on improving Magisk instead of drowning in the everlasting cat-and-mouse game 😉.
|
||||||
|
|
||||||
|
### Sunsetting Magisk-Modules-Repo
|
||||||
|
|
||||||
|
Due to lack of time and maintenance, the centralized Magisk-Modules-Repo was frozen, and the functionality to download modules from the repo is removed in v24.0. As a supplement, module developers can now specify an `updateJson` URL in their modules. The Magisk app will use that to check, download, and install module updates.
|
||||||
|
|
||||||
|
### Introducing Zygisk
|
||||||
|
|
||||||
|
Zygisk is **Magisk in Zygote**, the next big thing for Magisk! When this feature is enabled, a part of Magisk will run in the `Zygote` daemon process, allowing module developers to run code directly in every Android apps' processes. If you've heard of [Riru](https://github.com/RikkaApps/Riru), then Zygisk is inspired by that project and is functionally similar, though the implementation is quite different internally. I cannot wait to see what module developers can achieve using Zygisk!
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
For developers, details about `updateJson` and building Zygisk modules can all be found in the updated [documentation](https://topjohnwu.github.io/Magisk/guides.html#magisk-modules).
|
||||||
|
|
||||||
|
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
19
docs/releases/24200.md
Normal file
19
docs/releases/24200.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
## 2022.3.1 Magisk v24.2
|
||||||
|
|
||||||
|
Maintenance release fixing various issues.
|
||||||
|
|
||||||
|
- [MagiskSU] Fix buffer overflow
|
||||||
|
- [MagiskSU] Fix owner managed multiuser superuser settings
|
||||||
|
- [MagiskSU] Fix command logging when using `su -c <cmd>`
|
||||||
|
- [MagiskSU] Prevent su request indefinite blocking
|
||||||
|
- [MagiskBoot] Support `lz4_legacy` archive with multiple magic
|
||||||
|
- [MagiskBoot] Fix `lz4_lg` compression
|
||||||
|
- [DenyList] Allow targeting processes running as system UID
|
||||||
|
- [Zygisk] Workaround Samsung's "early zygote"
|
||||||
|
- [Zygisk] Improved Zygisk loading mechanism
|
||||||
|
- [Zygisk] Fix application UID tracking
|
||||||
|
- [Zygisk] Fix improper `umask` being set in zygote
|
||||||
|
- [App] Fix BusyBox execution test
|
||||||
|
- [App] Improve stub loading mechanism
|
||||||
|
- [App] Major app upgrade flow improvements
|
||||||
|
- [General] Improve commandline error handling and messaging
|
@@ -1,5 +1,7 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
- [v24.2](24200.md)
|
||||||
|
- [v24.1](24100.md)
|
||||||
- [v24.0](24000.md)
|
- [v24.0](24000.md)
|
||||||
- [v23.0](23000.md)
|
- [v23.0](23000.md)
|
||||||
- [v22.1](22100.md)
|
- [v22.1](22100.md)
|
||||||
|
@@ -24,9 +24,10 @@ org.gradle.caching=true
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.databinding.incremental=true
|
android.databinding.incremental=true
|
||||||
android.injected.testOnly=false
|
android.injected.testOnly=false
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
|
|
||||||
# Magisk
|
# Magisk
|
||||||
magisk.stubVersion=23
|
magisk.stubVersion=26
|
||||||
magisk.versionCode=24000
|
magisk.versionCode=24200
|
||||||
magisk.ndkVersion=21e
|
magisk.ndkVersion=23b
|
||||||
magisk.fullNdkVersion=21.4.7075529
|
magisk.fullNdkVersion=23.1.7779620
|
||||||
|
@@ -8,7 +8,12 @@ ifdef B_MAGISK
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := magisk
|
LOCAL_MODULE := magisk
|
||||||
LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils-shared libphmap libxhook
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libutils \
|
||||||
|
libnanopb \
|
||||||
|
libsystemproperties \
|
||||||
|
libphmap \
|
||||||
|
libxhook
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
core/applets.cpp \
|
core/applets.cpp \
|
||||||
@@ -43,12 +48,14 @@ include $(BUILD_EXECUTABLE)
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
ifdef B_INIT
|
ifdef B_INIT
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := magiskinit
|
LOCAL_MODULE := magiskinit
|
||||||
LOCAL_STATIC_LIBRARIES := libsepol libxz libutils
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libutilx \
|
||||||
|
libsepol \
|
||||||
|
libxz
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
init/init.cpp \
|
init/init.cpp \
|
||||||
@@ -73,7 +80,15 @@ ifdef B_BOOT
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := magiskboot
|
LOCAL_MODULE := magiskboot
|
||||||
LOCAL_STATIC_LIBRARIES := libmincrypt liblzma liblz4 libbz2 libfdt libutils libz libzopfli
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libutilx \
|
||||||
|
libmincrypt \
|
||||||
|
liblzma \
|
||||||
|
liblz4 \
|
||||||
|
libbz2 \
|
||||||
|
libfdt \
|
||||||
|
libz \
|
||||||
|
libzopfli
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
magiskboot/main.cpp \
|
magiskboot/main.cpp \
|
||||||
@@ -95,7 +110,9 @@ ifdef B_POLICY
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := magiskpolicy
|
LOCAL_MODULE := magiskpolicy
|
||||||
LOCAL_STATIC_LIBRARIES := libsepol libutils
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libutilx \
|
||||||
|
libsepol
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
core/applet_stub.cpp \
|
core/applet_stub.cpp \
|
||||||
@@ -115,7 +132,10 @@ ifdef B_PROP
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := resetprop
|
LOCAL_MODULE := resetprop
|
||||||
LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libutilx \
|
||||||
|
libnanopb \
|
||||||
|
libsystemproperties
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
core/applet_stub.cpp \
|
core/applet_stub.cpp \
|
||||||
@@ -133,7 +153,10 @@ ifneq (,$(wildcard jni/test.cpp))
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := test
|
LOCAL_MODULE := test
|
||||||
LOCAL_STATIC_LIBRARIES := libutils-shared libphmap
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libutils \
|
||||||
|
libphmap
|
||||||
|
|
||||||
LOCAL_SRC_FILES := test.cpp
|
LOCAL_SRC_FILES := test.cpp
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
|
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
|
||||||
APP_CFLAGS := -Wall -Oz -fomit-frame-pointer -flto
|
APP_CFLAGS := -Wall -Oz -fomit-frame-pointer -flto
|
||||||
APP_LDFLAGS := -flto
|
APP_LDFLAGS := -flto
|
||||||
APP_CPPFLAGS := -std=c++17
|
APP_CPPFLAGS := -std=c++20
|
||||||
APP_STL := none
|
APP_STL := none
|
||||||
APP_PLATFORM := android-21
|
APP_PLATFORM := android-21
|
||||||
APP_THIN_ARCHIVE := true
|
APP_THIN_ARCHIVE := true
|
||||||
APP_STRIP_MODE := --strip-all
|
APP_STRIP_MODE := --strip-all
|
||||||
|
|
||||||
ifneq ($(TARGET_ARCH),arm64)
|
|
||||||
ifneq ($(TARGET_ARCH),x86_64)
|
|
||||||
ifndef B_SHARED
|
ifndef B_SHARED
|
||||||
# Disable fortify on static 32-bit targets
|
# Fix static variables' ctor/dtor when using LTO
|
||||||
APP_CFLAGS += -D_FORTIFY_SOURCE=0 -Wno-macro-redefined
|
# See: https://github.com/android/ndk/issues/1461
|
||||||
endif
|
APP_LDFLAGS += -T jni/lto_fix.lds
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Busybox should use stock libc.a
|
# Busybox should use stock libc.a
|
||||||
ifdef B_BB
|
ifdef B_BB
|
||||||
APP_PLATFORM := android-22
|
APP_PLATFORM := android-24
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
APP_SHORT_COMMANDS := true
|
APP_SHORT_COMMANDS := true
|
||||||
endif
|
endif
|
||||||
|
@@ -21,20 +21,23 @@ static int call_applet(int argc, char *argv[]) {
|
|||||||
return (*applet_mains[i])(argc, argv);
|
return (*applet_mains[i])(argc, argv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (str_starts(base, "app_process")) {
|
|
||||||
return app_process_main(argc, argv);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "%s: applet not found\n", base.data());
|
fprintf(stderr, "%s: applet not found\n", base.data());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
umask(0);
|
|
||||||
enable_selinux();
|
enable_selinux();
|
||||||
cmdline_logging();
|
cmdline_logging();
|
||||||
init_argv0(argc, argv);
|
init_argv0(argc, argv);
|
||||||
|
|
||||||
string_view base = basename(argv[0]);
|
string_view base = basename(argv[0]);
|
||||||
|
|
||||||
|
// app_process is actually not an applet
|
||||||
|
if (str_starts(base, "app_process")) {
|
||||||
|
return app_process_main(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
umask(0);
|
||||||
if (base == "magisk" || base == "magisk32" || base == "magisk64") {
|
if (base == "magisk" || base == "magisk32" || base == "magisk64") {
|
||||||
if (argc > 1 && argv[1][0] != '-') {
|
if (argc > 1 && argv[1][0] != '-') {
|
||||||
// Calling applet via magisk [applet] args
|
// Calling applet via magisk [applet] args
|
||||||
|
@@ -25,22 +25,36 @@ bool zygisk_enabled = false;
|
|||||||
*********/
|
*********/
|
||||||
|
|
||||||
#define MNT_DIR_IS(dir) (me->mnt_dir == string_view(dir))
|
#define MNT_DIR_IS(dir) (me->mnt_dir == string_view(dir))
|
||||||
|
#define MNT_TYPE_IS(type) (me->mnt_type == string_view(type))
|
||||||
#define SETMIR(b, part) snprintf(b, sizeof(b), "%s/" MIRRDIR "/" #part, MAGISKTMP.data())
|
#define SETMIR(b, part) snprintf(b, sizeof(b), "%s/" MIRRDIR "/" #part, MAGISKTMP.data())
|
||||||
#define SETBLK(b, part) snprintf(b, sizeof(b), "%s/" BLOCKDIR "/" #part, MAGISKTMP.data())
|
#define SETBLK(b, part) snprintf(b, sizeof(b), "%s/" BLOCKDIR "/" #part, MAGISKTMP.data())
|
||||||
|
|
||||||
#define do_mount_mirror(part, flag) {\
|
#define do_mount_mirror(part) { \
|
||||||
SETMIR(buf1, part); \
|
SETMIR(buf1, part); \
|
||||||
SETBLK(buf2, part); \
|
SETBLK(buf2, part); \
|
||||||
unlink(buf2); \
|
unlink(buf2); \
|
||||||
mknod(buf2, S_IFBLK | 0600, st.st_dev); \
|
mknod(buf2, S_IFBLK | 0600, st.st_dev); \
|
||||||
xmkdir(buf1, 0755); \
|
xmkdir(buf1, 0755); \
|
||||||
xmount(buf2, buf1, me->mnt_type, flag, nullptr); \
|
int flags = 0; \
|
||||||
LOGI("mount: %s\n", buf1); \
|
auto opts = split_ro(me->mnt_opts, ",");\
|
||||||
|
for (string_view s : opts) { \
|
||||||
|
if (s == "ro") { \
|
||||||
|
flags |= MS_RDONLY; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
xmount(buf2, buf1, me->mnt_type, flags, nullptr); \
|
||||||
|
LOGI("mount: %s\n", buf1); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define mount_mirror(part, flag) \
|
#define mount_mirror(part) \
|
||||||
else if (MNT_DIR_IS("/" #part) && me->mnt_type != "tmpfs"sv && me->mnt_type != "overlay"sv && lstat(me->mnt_dir, &st) == 0) \
|
if (MNT_DIR_IS("/" #part) \
|
||||||
do_mount_mirror(part, flag)
|
&& !MNT_TYPE_IS("tmpfs") \
|
||||||
|
&& !MNT_TYPE_IS("overlay") \
|
||||||
|
&& lstat(me->mnt_dir, &st) == 0) { \
|
||||||
|
do_mount_mirror(part); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
#define link_mirror(part) \
|
#define link_mirror(part) \
|
||||||
SETMIR(buf1, part); \
|
SETMIR(buf1, part); \
|
||||||
@@ -50,11 +64,12 @@ if (access("/system/" #part, F_OK) == 0 && access(buf1, F_OK) != 0) { \
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define link_orig_dir(dir, part) \
|
#define link_orig_dir(dir, part) \
|
||||||
else if (MNT_DIR_IS(dir) && me->mnt_type != "tmpfs"sv && me->mnt_type != "overlay"sv) { \
|
if (MNT_DIR_IS(dir) && !MNT_TYPE_IS("tmpfs") && !MNT_TYPE_IS("overlay")) { \
|
||||||
SETMIR(buf1, part); \
|
SETMIR(buf1, part); \
|
||||||
rmdir(buf1); \
|
rmdir(buf1); \
|
||||||
xsymlink(dir, buf1); \
|
xsymlink(dir, buf1); \
|
||||||
LOGI("link: %s\n", buf1); \
|
LOGI("link: %s\n", buf1); \
|
||||||
|
break; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define link_orig(part) link_orig_dir("/" #part, part)
|
#define link_orig(part) link_orig_dir("/" #part, part)
|
||||||
@@ -66,20 +81,22 @@ static void mount_mirrors() {
|
|||||||
LOGI("* Mounting mirrors\n");
|
LOGI("* Mounting mirrors\n");
|
||||||
|
|
||||||
parse_mnt("/proc/mounts", [&](mntent *me) {
|
parse_mnt("/proc/mounts", [&](mntent *me) {
|
||||||
struct stat st;
|
struct stat st{};
|
||||||
if (0) {}
|
do {
|
||||||
mount_mirror(system, MS_RDONLY)
|
mount_mirror(system)
|
||||||
mount_mirror(vendor, MS_RDONLY)
|
mount_mirror(vendor)
|
||||||
mount_mirror(product, MS_RDONLY)
|
mount_mirror(product)
|
||||||
mount_mirror(system_ext, MS_RDONLY)
|
mount_mirror(system_ext)
|
||||||
mount_mirror(data, 0)
|
mount_mirror(data)
|
||||||
link_orig(cache)
|
link_orig(cache)
|
||||||
link_orig(metadata)
|
link_orig(metadata)
|
||||||
link_orig(persist)
|
link_orig(persist)
|
||||||
link_orig_dir("/mnt/vendor/persist", persist)
|
link_orig_dir("/mnt/vendor/persist", persist)
|
||||||
else if (SDK_INT >= 24 && MNT_DIR_IS("/proc") && !strstr(me->mnt_opts, "hidepid=2")) {
|
if (SDK_INT >= 24 && MNT_DIR_IS("/proc") && !strstr(me->mnt_opts, "hidepid=2")) {
|
||||||
xmount(nullptr, "/proc", nullptr, MS_REMOUNT, "hidepid=2,gid=3009");
|
xmount(nullptr, "/proc", nullptr, MS_REMOUNT, "hidepid=2,gid=3009");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
SETMIR(buf1, system);
|
SETMIR(buf1, system);
|
||||||
@@ -89,7 +106,7 @@ static void mount_mirrors() {
|
|||||||
parse_mnt("/proc/mounts", [&](mntent *me) {
|
parse_mnt("/proc/mounts", [&](mntent *me) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (MNT_DIR_IS("/") && me->mnt_type != "rootfs"sv && stat("/", &st) == 0) {
|
if (MNT_DIR_IS("/") && me->mnt_type != "rootfs"sv && stat("/", &st) == 0) {
|
||||||
do_mount_mirror(system_root, MS_RDONLY)
|
do_mount_mirror(system_root)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -256,6 +273,7 @@ static bool check_key_combo() {
|
|||||||
***********************/
|
***********************/
|
||||||
|
|
||||||
static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
extern int disable_deny();
|
||||||
|
|
||||||
void post_fs_data(int client) {
|
void post_fs_data(int client) {
|
||||||
// ack
|
// ack
|
||||||
|
@@ -86,20 +86,20 @@ void clear_poll() {
|
|||||||
static void poll_ctrl_handler(pollfd *pfd) {
|
static void poll_ctrl_handler(pollfd *pfd) {
|
||||||
int code = read_int(pfd->fd);
|
int code = read_int(pfd->fd);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case POLL_CTRL_NEW: {
|
case POLL_CTRL_NEW: {
|
||||||
pollfd new_fd;
|
pollfd new_fd;
|
||||||
poll_callback cb;
|
poll_callback cb;
|
||||||
xxread(pfd->fd, &new_fd, sizeof(new_fd));
|
xxread(pfd->fd, &new_fd, sizeof(new_fd));
|
||||||
xxread(pfd->fd, &cb, sizeof(cb));
|
xxread(pfd->fd, &cb, sizeof(cb));
|
||||||
register_poll(&new_fd, cb);
|
register_poll(&new_fd, cb);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case POLL_CTRL_RM: {
|
case POLL_CTRL_RM: {
|
||||||
int fd = read_int(pfd->fd);
|
int fd = read_int(pfd->fd);
|
||||||
bool auto_close = read_int(pfd->fd);
|
bool auto_close = read_int(pfd->fd);
|
||||||
unregister_poll(fd, auto_close);
|
unregister_poll(fd, auto_close);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,59 +135,60 @@ static void poll_ctrl_handler(pollfd *pfd) {
|
|||||||
|
|
||||||
static void handle_request_async(int client, int code, const sock_cred &cred) {
|
static void handle_request_async(int client, int code, const sock_cred &cred) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case DENYLIST:
|
case MainRequest::DENYLIST:
|
||||||
denylist_handler(client, &cred);
|
denylist_handler(client, &cred);
|
||||||
break;
|
break;
|
||||||
case SUPERUSER:
|
case MainRequest::SUPERUSER:
|
||||||
su_daemon_handler(client, &cred);
|
su_daemon_handler(client, &cred);
|
||||||
break;
|
break;
|
||||||
case POST_FS_DATA:
|
case MainRequest::POST_FS_DATA:
|
||||||
post_fs_data(client);
|
post_fs_data(client);
|
||||||
break;
|
break;
|
||||||
case LATE_START:
|
case MainRequest::LATE_START:
|
||||||
late_start(client);
|
late_start(client);
|
||||||
break;
|
break;
|
||||||
case BOOT_COMPLETE:
|
case MainRequest::BOOT_COMPLETE:
|
||||||
boot_complete(client);
|
boot_complete(client);
|
||||||
break;
|
break;
|
||||||
case SQLITE_CMD:
|
case MainRequest::SQLITE_CMD:
|
||||||
exec_sql(client);
|
exec_sql(client);
|
||||||
break;
|
break;
|
||||||
case REMOVE_MODULES:
|
case MainRequest::REMOVE_MODULES:
|
||||||
remove_modules();
|
remove_modules();
|
||||||
write_int(client, 0);
|
write_int(client, 0);
|
||||||
close(client);
|
close(client);
|
||||||
reboot();
|
reboot();
|
||||||
break;
|
break;
|
||||||
case ZYGISK_REQUEST:
|
case MainRequest::ZYGISK:
|
||||||
case ZYGISK_PASSTHROUGH:
|
case MainRequest::ZYGISK_PASSTHROUGH:
|
||||||
zygisk_handler(client, &cred);
|
zygisk_handler(client, &cred);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
close(client);
|
__builtin_unreachable();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_request_sync(int client, int code) {
|
static void handle_request_sync(int client, int code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case CHECK_VERSION:
|
case MainRequest::CHECK_VERSION:
|
||||||
write_string(client, MAGISK_VERSION ":MAGISK");
|
write_string(client, MAGISK_VERSION ":MAGISK");
|
||||||
break;
|
break;
|
||||||
case CHECK_VERSION_CODE:
|
case MainRequest::CHECK_VERSION_CODE:
|
||||||
write_int(client, MAGISK_VER_CODE);
|
write_int(client, MAGISK_VER_CODE);
|
||||||
break;
|
break;
|
||||||
case GET_PATH:
|
case MainRequest::GET_PATH:
|
||||||
write_string(client, MAGISKTMP.data());
|
write_string(client, MAGISKTMP.data());
|
||||||
break;
|
break;
|
||||||
case START_DAEMON:
|
case MainRequest::START_DAEMON:
|
||||||
setup_logfile(true);
|
setup_logfile(true);
|
||||||
break;
|
break;
|
||||||
case STOP_DAEMON:
|
case MainRequest::STOP_DAEMON:
|
||||||
denylist_handler(-1, nullptr);
|
denylist_handler(-1, nullptr);
|
||||||
write_int(client, 0);
|
write_int(client, 0);
|
||||||
// Terminate the daemon!
|
// Terminate the daemon!
|
||||||
exit(0);
|
exit(0);
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +196,7 @@ static bool is_client(pid_t pid) {
|
|||||||
// Verify caller is the same as server
|
// Verify caller is the same as server
|
||||||
char path[32];
|
char path[32];
|
||||||
sprintf(path, "/proc/%d/exe", pid);
|
sprintf(path, "/proc/%d/exe", pid);
|
||||||
struct stat st;
|
struct stat st{};
|
||||||
return !(stat(path, &st) || st.st_dev != self_st.st_dev || st.st_ino != self_st.st_ino);
|
return !(stat(path, &st) || st.st_dev != self_st.st_dev || st.st_ino != self_st.st_ino);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,52 +209,64 @@ static void handle_request(pollfd *pfd) {
|
|||||||
bool is_zygote;
|
bool is_zygote;
|
||||||
int code;
|
int code;
|
||||||
|
|
||||||
if (!get_client_cred(client, &cred))
|
if (!get_client_cred(client, &cred)) {
|
||||||
|
// Client died
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
is_root = cred.uid == UID_ROOT;
|
is_root = cred.uid == UID_ROOT;
|
||||||
is_zygote = cred.context == "u:r:zygote:s0";
|
is_zygote = cred.context == "u:r:zygote:s0";
|
||||||
|
|
||||||
if (!is_root && !is_zygote && !is_client(cred.pid))
|
if (!is_root && !is_zygote && !is_client(cred.pid)) {
|
||||||
|
// Unsupported client state
|
||||||
|
write_int(client, MainResponse::ACCESS_DENIED);
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
code = read_int(client);
|
code = read_int(client);
|
||||||
if (code < 0 || (code & DAEMON_CODE_MASK) >= DAEMON_CODE_END)
|
if (code < 0 || code >= MainRequest::END || code == MainRequest::_SYNC_BARRIER_) {
|
||||||
|
// Unknown request code
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
// Check client permissions
|
// Check client permissions
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case POST_FS_DATA:
|
case MainRequest::POST_FS_DATA:
|
||||||
case LATE_START:
|
case MainRequest::LATE_START:
|
||||||
case BOOT_COMPLETE:
|
case MainRequest::BOOT_COMPLETE:
|
||||||
case SQLITE_CMD:
|
case MainRequest::SQLITE_CMD:
|
||||||
case GET_PATH:
|
case MainRequest::GET_PATH:
|
||||||
case DENYLIST:
|
case MainRequest::DENYLIST:
|
||||||
case STOP_DAEMON:
|
case MainRequest::STOP_DAEMON:
|
||||||
if (!is_root) {
|
if (!is_root) {
|
||||||
write_int(client, ROOT_REQUIRED);
|
write_int(client, MainResponse::ROOT_REQUIRED);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REMOVE_MODULES:
|
case MainRequest::REMOVE_MODULES:
|
||||||
if (!is_root && cred.uid != UID_SHELL) {
|
if (!is_root && cred.uid != UID_SHELL) {
|
||||||
write_int(client, 1);
|
write_int(client, MainResponse::ACCESS_DENIED);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZYGISK_REQUEST:
|
case MainRequest::ZYGISK:
|
||||||
if (!is_zygote) {
|
if (!is_zygote) {
|
||||||
write_int(client, DAEMON_ERROR);
|
// Invalid client context
|
||||||
|
write_int(client, MainResponse::ACCESS_DENIED);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code & SYNC_FLAG) {
|
write_int(client, MainResponse::OK);
|
||||||
|
|
||||||
|
if (code < MainRequest::_SYNC_BARRIER_) {
|
||||||
handle_request_sync(client, code);
|
handle_request_sync(client, code);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle complex requests in another thread
|
// Handle async requests in another thread
|
||||||
exec_task([=] { handle_request_async(client, code, cred); });
|
exec_task([=] { handle_request_async(client, code, cred); });
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -308,9 +321,11 @@ static void daemon_entry() {
|
|||||||
// Escape from cgroup
|
// Escape from cgroup
|
||||||
int pid = getpid();
|
int pid = getpid();
|
||||||
switch_cgroup("/acct", pid);
|
switch_cgroup("/acct", pid);
|
||||||
switch_cgroup("/dev/memcg/apps", pid);
|
|
||||||
switch_cgroup("/dev/cg2_bpf", pid);
|
switch_cgroup("/dev/cg2_bpf", pid);
|
||||||
switch_cgroup("/sys/fs/cgroup", pid);
|
switch_cgroup("/sys/fs/cgroup", pid);
|
||||||
|
if (getprop("ro.config.per_app_memcg") != "false") {
|
||||||
|
switch_cgroup("/dev/memcg/apps", pid);
|
||||||
|
}
|
||||||
|
|
||||||
// Get self stat
|
// Get self stat
|
||||||
char buf[64];
|
char buf[64];
|
||||||
@@ -374,7 +389,7 @@ static void daemon_entry() {
|
|||||||
sockaddr_un sun{};
|
sockaddr_un sun{};
|
||||||
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
|
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
|
||||||
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
if (xbind(fd, (sockaddr*) &sun, len))
|
if (xbind(fd, (sockaddr *) &sun, len))
|
||||||
exit(1);
|
exit(1);
|
||||||
xlisten(fd, 10);
|
xlisten(fd, 10);
|
||||||
|
|
||||||
@@ -389,11 +404,11 @@ static void daemon_entry() {
|
|||||||
poll_loop();
|
poll_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int connect_daemon(bool create) {
|
int connect_daemon(int req, bool create) {
|
||||||
sockaddr_un sun{};
|
sockaddr_un sun{};
|
||||||
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
|
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
|
||||||
int fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
int fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
if (connect(fd, (sockaddr*) &sun, len)) {
|
if (connect(fd, (sockaddr *) &sun, len)) {
|
||||||
if (!create || getuid() != UID_ROOT) {
|
if (!create || getuid() != UID_ROOT) {
|
||||||
LOGE("No daemon is currently running!\n");
|
LOGE("No daemon is currently running!\n");
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -405,8 +420,27 @@ int connect_daemon(bool create) {
|
|||||||
daemon_entry();
|
daemon_entry();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (connect(fd, (struct sockaddr*) &sun, len))
|
while (connect(fd, (struct sockaddr *) &sun, len))
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
}
|
}
|
||||||
|
write_int(fd, req);
|
||||||
|
int res = read_int(fd);
|
||||||
|
if (res < MainResponse::ERROR || res >= MainResponse::END)
|
||||||
|
res = MainResponse::ERROR;
|
||||||
|
switch (res) {
|
||||||
|
case MainResponse::OK:
|
||||||
|
break;
|
||||||
|
case MainResponse::ERROR:
|
||||||
|
LOGE("Daemon error\n");
|
||||||
|
exit(-1);
|
||||||
|
case MainResponse::ROOT_REQUIRED:
|
||||||
|
LOGE("Root is required for this operation\n");
|
||||||
|
exit(-1);
|
||||||
|
case MainResponse::ACCESS_DENIED:
|
||||||
|
LOGE("Access denied\n");
|
||||||
|
exit(-1);
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
@@ -56,14 +56,12 @@ int magisk_main(int argc, char *argv[]) {
|
|||||||
printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n");
|
printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argv[1] == "-v"sv) {
|
} else if (argv[1] == "-v"sv) {
|
||||||
int fd = connect_daemon();
|
int fd = connect_daemon(MainRequest::CHECK_VERSION);
|
||||||
write_int(fd, CHECK_VERSION);
|
|
||||||
string v = read_string(fd);
|
string v = read_string(fd);
|
||||||
printf("%s\n", v.data());
|
printf("%s\n", v.data());
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argv[1] == "-V"sv) {
|
} else if (argv[1] == "-V"sv) {
|
||||||
int fd = connect_daemon();
|
int fd = connect_daemon(MainRequest::CHECK_VERSION_CODE);
|
||||||
write_int(fd, CHECK_VERSION_CODE);
|
|
||||||
printf("%d\n", read_int(fd));
|
printf("%d\n", read_int(fd));
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argv[1] == "--list"sv) {
|
} else if (argv[1] == "--list"sv) {
|
||||||
@@ -76,37 +74,32 @@ int magisk_main(int argc, char *argv[]) {
|
|||||||
} else if (argv[1] == "--restorecon"sv) {
|
} else if (argv[1] == "--restorecon"sv) {
|
||||||
restorecon();
|
restorecon();
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argc >= 4 && argv[1] == "--clone-attr"sv) {;
|
} else if (argc >= 4 && argv[1] == "--clone-attr"sv) {
|
||||||
clone_attr(argv[2], argv[3]);
|
clone_attr(argv[2], argv[3]);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argc >= 4 && argv[1] == "--clone"sv) {
|
} else if (argc >= 4 && argv[1] == "--clone"sv) {
|
||||||
cp_afc(argv[2], argv[3]);
|
cp_afc(argv[2], argv[3]);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argv[1] == "--daemon"sv) {
|
} else if (argv[1] == "--daemon"sv) {
|
||||||
int fd = connect_daemon(true);
|
int fd = connect_daemon(MainRequest::START_DAEMON, true);
|
||||||
write_int(fd, START_DAEMON);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (argv[1] == "--stop"sv) {
|
} else if (argv[1] == "--stop"sv) {
|
||||||
int fd = connect_daemon();
|
int fd = connect_daemon(MainRequest::STOP_DAEMON);
|
||||||
write_int(fd, STOP_DAEMON);
|
|
||||||
return read_int(fd);
|
return read_int(fd);
|
||||||
} else if (argv[1] == "--post-fs-data"sv) {
|
} else if (argv[1] == "--post-fs-data"sv) {
|
||||||
int fd = connect_daemon(true);
|
int fd = connect_daemon(MainRequest::POST_FS_DATA, true);
|
||||||
write_int(fd, POST_FS_DATA);
|
|
||||||
return read_int(fd);
|
return read_int(fd);
|
||||||
} else if (argv[1] == "--service"sv) {
|
} else if (argv[1] == "--service"sv) {
|
||||||
int fd = connect_daemon(true);
|
int fd = connect_daemon(MainRequest::LATE_START, true);
|
||||||
write_int(fd, LATE_START);
|
|
||||||
return read_int(fd);
|
return read_int(fd);
|
||||||
} else if (argv[1] == "--boot-complete"sv) {
|
} else if (argv[1] == "--boot-complete"sv) {
|
||||||
int fd = connect_daemon(true);
|
int fd = connect_daemon(MainRequest::BOOT_COMPLETE, true);
|
||||||
write_int(fd, BOOT_COMPLETE);
|
|
||||||
return read_int(fd);
|
return read_int(fd);
|
||||||
} else if (argv[1] == "--denylist"sv) {
|
} else if (argv[1] == "--denylist"sv) {
|
||||||
return denylist_cli(argc - 1, argv + 1);
|
return denylist_cli(argc - 1, argv + 1);
|
||||||
}else if (argc >= 3 && argv[1] == "--sqlite"sv) {
|
} else if (argc >= 3 && argv[1] == "--sqlite"sv) {
|
||||||
int fd = connect_daemon();
|
int fd = connect_daemon(MainRequest::SQLITE_CMD);
|
||||||
write_int(fd, SQLITE_CMD);
|
|
||||||
write_string(fd, argv[2]);
|
write_string(fd, argv[2]);
|
||||||
string res;
|
string res;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -116,12 +109,10 @@ int magisk_main(int argc, char *argv[]) {
|
|||||||
printf("%s\n", res.data());
|
printf("%s\n", res.data());
|
||||||
}
|
}
|
||||||
} else if (argv[1] == "--remove-modules"sv) {
|
} else if (argv[1] == "--remove-modules"sv) {
|
||||||
int fd = connect_daemon();
|
int fd = connect_daemon(MainRequest::REMOVE_MODULES);
|
||||||
write_int(fd, REMOVE_MODULES);
|
|
||||||
return read_int(fd);
|
return read_int(fd);
|
||||||
} else if (argv[1] == "--path"sv) {
|
} else if (argv[1] == "--path"sv) {
|
||||||
int fd = connect_daemon();
|
int fd = connect_daemon(MainRequest::GET_PATH);
|
||||||
write_int(fd, GET_PATH);
|
|
||||||
string path = read_string(fd);
|
string path = read_string(fd);
|
||||||
printf("%s\n", path.data());
|
printf("%s\n", path.data());
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -15,7 +15,6 @@ using namespace std;
|
|||||||
|
|
||||||
static void restore_syscon(int dirfd) {
|
static void restore_syscon(int dirfd) {
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
DIR *dir;
|
|
||||||
char *con;
|
char *con;
|
||||||
|
|
||||||
if (fgetfilecon(dirfd, &con) >= 0) {
|
if (fgetfilecon(dirfd, &con) >= 0) {
|
||||||
@@ -24,11 +23,12 @@ static void restore_syscon(int dirfd) {
|
|||||||
freecon(con);
|
freecon(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
dir = xfdopendir(dirfd);
|
auto dir = xopen_dir(dirfd);
|
||||||
while ((entry = xreaddir(dir))) {
|
while ((entry = xreaddir(dir.get()))) {
|
||||||
int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||||
if (entry->d_type == DT_DIR) {
|
if (entry->d_type == DT_DIR) {
|
||||||
restore_syscon(fd);
|
restore_syscon(fd);
|
||||||
|
continue;
|
||||||
} else if (entry->d_type == DT_REG) {
|
} else if (entry->d_type == DT_REG) {
|
||||||
if (fgetfilecon(fd, &con) >= 0) {
|
if (fgetfilecon(fd, &con) >= 0) {
|
||||||
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0 || strcmp(con, ADB_CON) == 0)
|
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0 || strcmp(con, ADB_CON) == 0)
|
||||||
@@ -47,16 +47,16 @@ static void restore_syscon(int dirfd) {
|
|||||||
|
|
||||||
static void restore_magiskcon(int dirfd) {
|
static void restore_magiskcon(int dirfd) {
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
DIR *dir;
|
|
||||||
|
|
||||||
fsetfilecon(dirfd, MAGISK_CON);
|
fsetfilecon(dirfd, MAGISK_CON);
|
||||||
fchown(dirfd, 0, 0);
|
fchown(dirfd, 0, 0);
|
||||||
|
|
||||||
dir = xfdopendir(dirfd);
|
auto dir = xopen_dir(dirfd);
|
||||||
while ((entry = xreaddir(dir))) {
|
while ((entry = xreaddir(dir.get()))) {
|
||||||
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||||
if (entry->d_type == DT_DIR) {
|
if (entry->d_type == DT_DIR) {
|
||||||
restore_magiskcon(fd);
|
restore_magiskcon(fd);
|
||||||
|
continue;
|
||||||
} else if (entry->d_type) {
|
} else if (entry->d_type) {
|
||||||
fsetfilecon(fd, MAGISK_CON);
|
fsetfilecon(fd, MAGISK_CON);
|
||||||
fchown(fd, 0, 0);
|
fchown(fd, 0, 0);
|
||||||
@@ -71,12 +71,8 @@ void restorecon() {
|
|||||||
lsetfilecon(SECURE_DIR, ADB_CON);
|
lsetfilecon(SECURE_DIR, ADB_CON);
|
||||||
close(fd);
|
close(fd);
|
||||||
lsetfilecon(MODULEROOT, SYSTEM_CON);
|
lsetfilecon(MODULEROOT, SYSTEM_CON);
|
||||||
fd = xopen(MODULEROOT, O_RDONLY | O_CLOEXEC);
|
restore_syscon(xopen(MODULEROOT, O_RDONLY | O_CLOEXEC));
|
||||||
restore_syscon(fd);
|
restore_magiskcon(xopen(DATABIN, O_RDONLY | O_CLOEXEC));
|
||||||
close(fd);
|
|
||||||
fd = xopen(DATABIN, O_RDONLY | O_CLOEXEC);
|
|
||||||
restore_magiskcon(fd);
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void restore_tmpcon() {
|
void restore_tmpcon() {
|
||||||
|
7
native/jni/external/Android.mk
vendored
7
native/jni/external/Android.mk
vendored
@@ -191,7 +191,6 @@ LOCAL_SRC_FILES := \
|
|||||||
selinux/libsepol/src/context.c \
|
selinux/libsepol/src/context.c \
|
||||||
selinux/libsepol/src/context_record.c \
|
selinux/libsepol/src/context_record.c \
|
||||||
selinux/libsepol/src/debug.c \
|
selinux/libsepol/src/debug.c \
|
||||||
selinux/libsepol/src/deprecated_funcs.c \
|
|
||||||
selinux/libsepol/src/ebitmap.c \
|
selinux/libsepol/src/ebitmap.c \
|
||||||
selinux/libsepol/src/expand.c \
|
selinux/libsepol/src/expand.c \
|
||||||
selinux/libsepol/src/handle.c \
|
selinux/libsepol/src/handle.c \
|
||||||
@@ -217,9 +216,9 @@ LOCAL_SRC_FILES := \
|
|||||||
selinux/libsepol/src/policydb.c \
|
selinux/libsepol/src/policydb.c \
|
||||||
selinux/libsepol/src/policydb_convert.c \
|
selinux/libsepol/src/policydb_convert.c \
|
||||||
selinux/libsepol/src/policydb_public.c \
|
selinux/libsepol/src/policydb_public.c \
|
||||||
|
selinux/libsepol/src/policydb_validate.c \
|
||||||
selinux/libsepol/src/port_record.c \
|
selinux/libsepol/src/port_record.c \
|
||||||
selinux/libsepol/src/ports.c \
|
selinux/libsepol/src/ports.c \
|
||||||
selinux/libsepol/src/roles.c \
|
|
||||||
selinux/libsepol/src/services.c \
|
selinux/libsepol/src/services.c \
|
||||||
selinux/libsepol/src/sidtab.c \
|
selinux/libsepol/src/sidtab.c \
|
||||||
selinux/libsepol/src/symtab.c \
|
selinux/libsepol/src/symtab.c \
|
||||||
@@ -246,7 +245,9 @@ LOCAL_SRC_FILES := \
|
|||||||
selinux/libsepol/cil/src/cil_strpool.c \
|
selinux/libsepol/cil/src/cil_strpool.c \
|
||||||
selinux/libsepol/cil/src/cil_symtab.c \
|
selinux/libsepol/cil/src/cil_symtab.c \
|
||||||
selinux/libsepol/cil/src/cil_tree.c \
|
selinux/libsepol/cil/src/cil_tree.c \
|
||||||
selinux/libsepol/cil/src/cil_verify.c
|
selinux/libsepol/cil/src/cil_verify.c \
|
||||||
|
selinux/libsepol/cil/src/cil_write_ast.c
|
||||||
|
|
||||||
LOCAL_CFLAGS := -Dgetline=__getline -Wno-implicit-function-declaration
|
LOCAL_CFLAGS := -Dgetline=__getline -Wno-implicit-function-declaration
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
2
native/jni/external/busybox
vendored
2
native/jni/external/busybox
vendored
Submodule native/jni/external/busybox updated: c4f0c98aeb...e68ef09273
2
native/jni/external/libcxx
vendored
2
native/jni/external/libcxx
vendored
Submodule native/jni/external/libcxx updated: b74fd5905f...0aa67c3ffe
2
native/jni/external/selinux
vendored
2
native/jni/external/selinux
vendored
Submodule native/jni/external/selinux updated: 969114cff6...d0f582372b
@@ -384,21 +384,21 @@ bool prop_area::prune_node(prop_bt * const node) {
|
|||||||
bool is_leaf = true;
|
bool is_leaf = true;
|
||||||
if (atomic_load_explicit(&node->children, memory_order_relaxed) != 0) {
|
if (atomic_load_explicit(&node->children, memory_order_relaxed) != 0) {
|
||||||
if (prune_node(to_prop_bt(&node->children))) {
|
if (prune_node(to_prop_bt(&node->children))) {
|
||||||
atomic_store_explicit(&node->children, 0, memory_order_release);
|
atomic_store_explicit(&node->children, 0u, memory_order_release);
|
||||||
} else {
|
} else {
|
||||||
is_leaf = false;
|
is_leaf = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (atomic_load_explicit(&node->left, memory_order_relaxed) != 0) {
|
if (atomic_load_explicit(&node->left, memory_order_relaxed) != 0) {
|
||||||
if (prune_node(to_prop_bt(&node->left))) {
|
if (prune_node(to_prop_bt(&node->left))) {
|
||||||
atomic_store_explicit(&node->left, 0, memory_order_release);
|
atomic_store_explicit(&node->left, 0u, memory_order_release);
|
||||||
} else {
|
} else {
|
||||||
is_leaf = false;
|
is_leaf = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (atomic_load_explicit(&node->right, memory_order_relaxed) != 0) {
|
if (atomic_load_explicit(&node->right, memory_order_relaxed) != 0) {
|
||||||
if (prune_node(to_prop_bt(&node->right))) {
|
if (prune_node(to_prop_bt(&node->right))) {
|
||||||
atomic_store_explicit(&node->right, 0, memory_order_release);
|
atomic_store_explicit(&node->right, 0u, memory_order_release);
|
||||||
} else {
|
} else {
|
||||||
is_leaf = false;
|
is_leaf = false;
|
||||||
}
|
}
|
||||||
@@ -425,7 +425,7 @@ bool prop_area::rm(const char *name, bool trim_node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// De-reference the existing property ASAP
|
// De-reference the existing property ASAP
|
||||||
atomic_store_explicit(&node->prop, 0, memory_order_release);
|
atomic_store_explicit(&node->prop, 0u, memory_order_release);
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
// Wipe out the old info
|
// Wipe out the old info
|
||||||
|
@@ -9,38 +9,40 @@
|
|||||||
|
|
||||||
#include <socket.hpp>
|
#include <socket.hpp>
|
||||||
|
|
||||||
// Daemon command code flags/masks
|
|
||||||
enum : int {
|
|
||||||
SYNC_FLAG = (1 << 30),
|
|
||||||
DAEMON_CODE_MASK = std::numeric_limits<int>::max() >> 1
|
|
||||||
};
|
|
||||||
|
|
||||||
// Daemon command codes
|
// Daemon command codes
|
||||||
|
namespace MainRequest {
|
||||||
enum : int {
|
enum : int {
|
||||||
START_DAEMON = SYNC_FLAG | 0,
|
START_DAEMON,
|
||||||
CHECK_VERSION = SYNC_FLAG | 1,
|
CHECK_VERSION,
|
||||||
CHECK_VERSION_CODE = SYNC_FLAG | 2,
|
CHECK_VERSION_CODE,
|
||||||
GET_PATH = SYNC_FLAG | 3,
|
GET_PATH,
|
||||||
STOP_DAEMON = SYNC_FLAG | 4,
|
STOP_DAEMON,
|
||||||
SUPERUSER = 5,
|
|
||||||
|
_SYNC_BARRIER_,
|
||||||
|
|
||||||
|
SUPERUSER,
|
||||||
POST_FS_DATA,
|
POST_FS_DATA,
|
||||||
LATE_START,
|
LATE_START,
|
||||||
BOOT_COMPLETE,
|
BOOT_COMPLETE,
|
||||||
DENYLIST,
|
DENYLIST,
|
||||||
SQLITE_CMD,
|
SQLITE_CMD,
|
||||||
REMOVE_MODULES,
|
REMOVE_MODULES,
|
||||||
ZYGISK_REQUEST,
|
ZYGISK,
|
||||||
ZYGISK_PASSTHROUGH,
|
ZYGISK_PASSTHROUGH,
|
||||||
DAEMON_CODE_END,
|
END,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Return codes for daemon
|
// Return codes for daemon
|
||||||
|
namespace MainResponse {
|
||||||
enum : int {
|
enum : int {
|
||||||
DAEMON_ERROR = -1,
|
ERROR = -1,
|
||||||
DAEMON_SUCCESS = 0,
|
OK = 0,
|
||||||
ROOT_REQUIRED,
|
ROOT_REQUIRED,
|
||||||
DAEMON_LAST
|
ACCESS_DENIED,
|
||||||
|
END
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct module_info {
|
struct module_info {
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -55,7 +57,7 @@ extern int app_process_32;
|
|||||||
extern int app_process_64;
|
extern int app_process_64;
|
||||||
extern std::vector<module_info> *module_list;
|
extern std::vector<module_info> *module_list;
|
||||||
|
|
||||||
int connect_daemon(bool create = false);
|
int connect_daemon(int req, bool create = false);
|
||||||
|
|
||||||
// Poll control
|
// Poll control
|
||||||
using poll_callback = void(*)(pollfd*);
|
using poll_callback = void(*)(pollfd*);
|
||||||
@@ -81,5 +83,4 @@ void zygisk_handler(int client, const sock_cred *cred);
|
|||||||
|
|
||||||
// Denylist
|
// Denylist
|
||||||
void initialize_denylist();
|
void initialize_denylist();
|
||||||
int disable_deny();
|
|
||||||
int denylist_cli(int argc, char **argv);
|
int denylist_cli(int argc, char **argv);
|
||||||
|
@@ -31,6 +31,12 @@ static void patch_init_rc(const char *src, const char *dest, const char *tmp_dir
|
|||||||
fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
|
fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// Samsung's persist.sys.zygote.early will start zygotes before actual post-fs-data phase
|
||||||
|
if (str_starts(line, "on property:persist.sys.zygote.early=")) {
|
||||||
|
LOGD("Invalidate persist.sys.zygote.early\n");
|
||||||
|
fprintf(rc, "on property:persist.sys.zygote.early.xxxxx=true\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Else just write the line
|
// Else just write the line
|
||||||
fprintf(rc, "%s", line.data());
|
fprintf(rc, "%s", line.data());
|
||||||
return true;
|
return true;
|
||||||
@@ -234,7 +240,7 @@ void SARBase::patch_rootdir() {
|
|||||||
make_pair(SPLIT_PLAT_CIL, "xxx"), /* Force loading monolithic sepolicy */
|
make_pair(SPLIT_PLAT_CIL, "xxx"), /* Force loading monolithic sepolicy */
|
||||||
make_pair(MONOPOLICY, sepol) /* Redirect /sepolicy to custom path */
|
make_pair(MONOPOLICY, sepol) /* Redirect /sepolicy to custom path */
|
||||||
});
|
});
|
||||||
if (avd_hack) {
|
if constexpr (avd_hack) {
|
||||||
// Force disable early mount on original init
|
// Force disable early mount on original init
|
||||||
init.patch({ make_pair("android,fstab", "xxx") });
|
init.patch({ make_pair("android,fstab", "xxx") });
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,3 @@
|
|||||||
// Force using legacy_signal_inlines.h
|
|
||||||
#define __ANDROID_API_BACKUP__ __ANDROID_API__
|
|
||||||
#undef __ANDROID_API__
|
|
||||||
#define __ANDROID_API__ 20
|
|
||||||
#include <android/legacy_signal_inlines.h>
|
|
||||||
#undef __ANDROID_API__
|
|
||||||
#define __ANDROID_API__ __ANDROID_API_BACKUP__
|
|
||||||
|
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
12
native/jni/lto_fix.lds
Normal file
12
native/jni/lto_fix.lds
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
SECTIONS {
|
||||||
|
.init_array : {
|
||||||
|
*(SORT_BY_INIT_PRIORITY(.init_array.*))
|
||||||
|
*(EXCLUDE_FILE(*crtend_android.o) .init_array)
|
||||||
|
}
|
||||||
|
} INSERT AFTER .fini_array;
|
||||||
|
SECTIONS {
|
||||||
|
.fini_array : {
|
||||||
|
*(SORT_BY_INIT_PRIORITY(.fini_array.*))
|
||||||
|
*(EXCLUDE_FILE(*crtend_android.o) .fini_array)
|
||||||
|
}
|
||||||
|
} INSERT BEFORE .init_array;
|
@@ -438,7 +438,11 @@ public:
|
|||||||
|
|
||||||
~LZ4F_encoder() override {
|
~LZ4F_encoder() override {
|
||||||
size_t len = LZ4F_compressEnd(ctx, out_buf, outCapacity, nullptr);
|
size_t len = LZ4F_compressEnd(ctx, out_buf, outCapacity, nullptr);
|
||||||
bwrite(out_buf, len);
|
if (LZ4F_isError(len)) {
|
||||||
|
LOGE("LZ4F end of frame error: %s\n", LZ4F_getErrorName(len));
|
||||||
|
} else if (!bwrite(out_buf, len)) {
|
||||||
|
LOGE("LZ4F end of frame error: I/O error\n");
|
||||||
|
}
|
||||||
LZ4F_freeCompressionContext(ctx);
|
LZ4F_freeCompressionContext(ctx);
|
||||||
delete[] out_buf;
|
delete[] out_buf;
|
||||||
}
|
}
|
||||||
@@ -454,7 +458,7 @@ private:
|
|||||||
class LZ4_decoder : public chunk_out_stream {
|
class LZ4_decoder : public chunk_out_stream {
|
||||||
public:
|
public:
|
||||||
explicit LZ4_decoder(stream_ptr &&base) :
|
explicit LZ4_decoder(stream_ptr &&base) :
|
||||||
chunk_out_stream(std::move(base), LZ4_COMPRESSED, sizeof(block_sz) + 4),
|
chunk_out_stream(std::move(base), LZ4_COMPRESSED, sizeof(block_sz)),
|
||||||
out_buf(new char[LZ4_UNCOMPRESSED]), block_sz(0) {}
|
out_buf(new char[LZ4_UNCOMPRESSED]), block_sz(0) {}
|
||||||
|
|
||||||
~LZ4_decoder() override {
|
~LZ4_decoder() override {
|
||||||
@@ -471,12 +475,14 @@ protected:
|
|||||||
auto in = reinterpret_cast<const char *>(buf);
|
auto in = reinterpret_cast<const char *>(buf);
|
||||||
|
|
||||||
if (block_sz == 0) {
|
if (block_sz == 0) {
|
||||||
if (chunk_sz == sizeof(block_sz) + 4) {
|
memcpy(&block_sz, in, sizeof(block_sz));
|
||||||
// Skip the first 4 bytes, which is magic
|
if (block_sz == 0x184C2102) {
|
||||||
memcpy(&block_sz, in + 4, sizeof(block_sz));
|
// This is actually the lz4 magic, read the next 4 bytes
|
||||||
} else {
|
block_sz = 0;
|
||||||
memcpy(&block_sz, in, sizeof(block_sz));
|
chunk_sz = sizeof(block_sz);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
// Read the next block chunk
|
||||||
chunk_sz = block_sz;
|
chunk_sz = block_sz;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -520,7 +526,7 @@ protected:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (bwrite(&block_sz, sizeof(block_sz)) && bwrite(out_buf, block_sz)) {
|
if (bwrite(&block_sz, sizeof(block_sz)) && bwrite(out_buf, block_sz)) {
|
||||||
in_total += sizeof(block_sz) + block_sz;
|
in_total += len;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user