mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-16 05:10:02 +00:00
Compare commits
164 Commits
manager-v5
...
manager-v5
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b885ccbd63 | ||
![]() |
da6f1d0f12 | ||
![]() |
4c0d435b6b | ||
![]() |
0e717a2de4 | ||
![]() |
cada862214 | ||
![]() |
682c6d4e7b | ||
![]() |
d0a253c97e | ||
![]() |
c0e2b3027b | ||
![]() |
e7dc14b07d | ||
![]() |
0da9146e90 | ||
![]() |
ad05a33e02 | ||
![]() |
8224e038a3 | ||
![]() |
03c04c2141 | ||
![]() |
2e091b04e5 | ||
![]() |
60296493fe | ||
![]() |
20c20f8f9b | ||
![]() |
f1d642a4e5 | ||
![]() |
e0e5ea17a4 | ||
![]() |
91a0ba72dc | ||
![]() |
c54c5a974a | ||
![]() |
532b8c54ab | ||
![]() |
5ac87891b5 | ||
![]() |
2d905ce3fb | ||
![]() |
831112abd2 | ||
![]() |
153d0f5505 | ||
![]() |
c78896a335 | ||
![]() |
316ec98e0f | ||
![]() |
cf58545a45 | ||
![]() |
e86015badc | ||
![]() |
c8f65fc9a1 | ||
![]() |
7684602ea8 | ||
![]() |
4601989d4a | ||
![]() |
23f697d62b | ||
![]() |
4ff39f8817 | ||
![]() |
1df41003ec | ||
![]() |
1f39ee41ad | ||
![]() |
42d8b1ecb9 | ||
![]() |
a4da7b33e6 | ||
![]() |
e4ee9e9095 | ||
![]() |
77430a282f | ||
![]() |
e6c1dd532d | ||
![]() |
d1f301e059 | ||
![]() |
1e812c40ce | ||
![]() |
a949641342 | ||
![]() |
c231e88a5d | ||
![]() |
79c71509f6 | ||
![]() |
5dab580cfc | ||
![]() |
499a157946 | ||
![]() |
c5a7ab2415 | ||
![]() |
3dd5a6f378 | ||
![]() |
7be26a0677 | ||
![]() |
c183fdd3ca | ||
![]() |
baa439457e | ||
![]() |
4dbcd54b72 | ||
![]() |
11062f2d4f | ||
![]() |
a0466085fe | ||
![]() |
f2f7d77847 | ||
![]() |
b2105f2d88 | ||
![]() |
4126f3bdcb | ||
![]() |
74ccfe6088 | ||
![]() |
48085b5573 | ||
![]() |
7b9ddc9b3b | ||
![]() |
15726a759c | ||
![]() |
2c7474ea87 | ||
![]() |
c726aee643 | ||
![]() |
c3e94e1480 | ||
![]() |
5f1343e5b4 | ||
![]() |
ffb1303d61 | ||
![]() |
a0b0d938f0 | ||
![]() |
158f5ba7d9 | ||
![]() |
b8cf40161c | ||
![]() |
fb96e6a56f | ||
![]() |
6668ba2511 | ||
![]() |
4668ef3020 | ||
![]() |
630f2b7d19 | ||
![]() |
dde0a4a7c8 | ||
![]() |
b06f69573d | ||
![]() |
8fd03f7434 | ||
![]() |
90e4ac2d23 | ||
![]() |
956bceae75 | ||
![]() |
c663be86de | ||
![]() |
aca78baecf | ||
![]() |
fbcf6b7954 | ||
![]() |
84123222aa | ||
![]() |
e9dbcf693d | ||
![]() |
1cd0a9d48f | ||
![]() |
1b48e44914 | ||
![]() |
0a398f03fd | ||
![]() |
15ed3e52f2 | ||
![]() |
8990919dab | ||
![]() |
e5638e4b15 | ||
![]() |
404c6fac9a | ||
![]() |
267395bfa2 | ||
![]() |
920fc5ae99 | ||
![]() |
92ed0ae51b | ||
![]() |
6764a98409 | ||
![]() |
fd7b5f393a | ||
![]() |
2ca528f93f | ||
![]() |
ce2e6b7d35 | ||
![]() |
684c5d225a | ||
![]() |
b75018b03b | ||
![]() |
41499d4b3c | ||
![]() |
383c97c303 | ||
![]() |
74b54ef371 | ||
![]() |
bbf7b4db79 | ||
![]() |
c61f0acab5 | ||
![]() |
398af123b2 | ||
![]() |
315fa9d7d3 | ||
![]() |
fb5e8ef40c | ||
![]() |
e79d764148 | ||
![]() |
ebbee0dc43 | ||
![]() |
ed0c16e201 | ||
![]() |
209fdf349a | ||
![]() |
f49f2afacd | ||
![]() |
8c6330a3c4 | ||
![]() |
337b777125 | ||
![]() |
1b756e8d96 | ||
![]() |
52d478df1a | ||
![]() |
0c782edf21 | ||
![]() |
e3948d295e | ||
![]() |
5f2c742a5c | ||
![]() |
b30c77aab9 | ||
![]() |
a5916b9c49 | ||
![]() |
453180e30b | ||
![]() |
8bd432d391 | ||
![]() |
c9d3e20aef | ||
![]() |
d5408d1f09 | ||
![]() |
f334532aba | ||
![]() |
be77c09f3d | ||
![]() |
7de6a92753 | ||
![]() |
36f76f5a14 | ||
![]() |
b84523d557 | ||
![]() |
2c78c415e9 | ||
![]() |
79ccb30dd2 | ||
![]() |
3c566becf6 | ||
![]() |
151ca593af | ||
![]() |
4132eacba0 | ||
![]() |
06e6151816 | ||
![]() |
70277d4edd | ||
![]() |
d21d2f1a9c | ||
![]() |
74a7be996f | ||
![]() |
3f38579529 | ||
![]() |
4d5a9f6e15 | ||
![]() |
41f47acd76 | ||
![]() |
821dcaa7c7 | ||
![]() |
7135d26419 | ||
![]() |
f7fd354dce | ||
![]() |
0c69a65bc4 | ||
![]() |
2f2ca5eab4 | ||
![]() |
df9c40c035 | ||
![]() |
25b67017e4 | ||
![]() |
bc9c3346f3 | ||
![]() |
1db7e19fe8 | ||
![]() |
102c03ce2b | ||
![]() |
ec19eb4455 | ||
![]() |
6d9924d50e | ||
![]() |
16c4d74274 | ||
![]() |
e4af5fd36a | ||
![]() |
702775493a | ||
![]() |
b2ae826066 | ||
![]() |
cc3e9990fa | ||
![]() |
271cbddd5e | ||
![]() |
c1423ca9ad | ||
![]() |
74379150a1 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,7 +6,7 @@
|
|||||||
app/release
|
app/release
|
||||||
*.hprof
|
*.hprof
|
||||||
.externalNativeBuild/
|
.externalNativeBuild/
|
||||||
*.sh
|
src/full/res/raw/util_functions.sh
|
||||||
public.certificate.x509.pem
|
public.certificate.x509.pem
|
||||||
private.key.pk8
|
private.key.pk8
|
||||||
*.apk
|
*.apk
|
||||||
|
@@ -1,2 +1,7 @@
|
|||||||
# Magisk Manager
|
# Magisk Manager
|
||||||
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
|
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
The default (English) string resources are scattered in these files: `src/full/res/values/strings.xml`, `src/main/res/values/strings.xml`, `src/stub/res/values/strings.xml`.
|
||||||
|
Place the translated XMLs in the corresponding folder to the locale.
|
||||||
|
Translations are highly appreciated via pull requests here on Github.
|
||||||
|
50
build.gradle
50
build.gradle
@@ -1,15 +1,13 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
buildToolsVersion "27.0.3"
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.topjohnwu.magisk"
|
applicationId "com.topjohnwu.magisk"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 27
|
targetSdkVersion rootProject.ext.compileSdkVersion
|
||||||
versionCode 100
|
|
||||||
versionName "5.6.0"
|
|
||||||
javaCompileOptions {
|
javaCompileOptions {
|
||||||
annotationProcessorOptions {
|
annotationProcessorOptions {
|
||||||
argument('butterknife.debuggable', 'false')
|
argument('butterknife.debuggable', 'false')
|
||||||
@@ -21,9 +19,23 @@ android {
|
|||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions "mode"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
full {
|
||||||
|
versionCode 125
|
||||||
|
versionName "5.8.0"
|
||||||
|
}
|
||||||
|
stub {
|
||||||
|
versionCode 1
|
||||||
|
versionName "stub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
@@ -37,23 +49,17 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
jcenter()
|
|
||||||
google()
|
|
||||||
maven { url "https://jitpack.io" }
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation project(':utils')
|
fullImplementation project(':utils')
|
||||||
implementation 'com.github.topjohnwu:libsu:1.1.0'
|
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
|
||||||
implementation 'com.android.support:recyclerview-v7:27.0.2'
|
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
|
||||||
implementation 'com.android.support:cardview-v7:27.0.2'
|
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
|
||||||
implementation 'com.android.support:design:27.0.2'
|
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
|
||||||
implementation 'com.android.support:support-v4:27.0.2'
|
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
|
||||||
implementation 'com.jakewharton:butterknife:8.8.1'
|
fullImplementation 'com.github.topjohnwu:libsu:1.2.0'
|
||||||
implementation 'com.atlassian.commonmark:commonmark:0.10.0'
|
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
|
||||||
implementation 'org.kamranzafar:jtar:2.3'
|
fullImplementation 'org.kamranzafar:jtar:2.3'
|
||||||
implementation 'com.google.code.gson:gson:2.8.2'
|
fullImplementation 'com.jakewharton:butterknife:8.8.1'
|
||||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
||||||
}
|
}
|
||||||
|
82
src/full/AndroidManifest.xml
Normal file
82
src/full/AndroidManifest.xml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.topjohnwu.magisk">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".MagiskManager"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
android:exported="true" />
|
||||||
|
<activity
|
||||||
|
android:name=".SplashActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/SplashTheme">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".AboutActivity"
|
||||||
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".SettingsActivity"
|
||||||
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".FlashActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:screenOrientation="nosensor"
|
||||||
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".NoUIActivity"
|
||||||
|
android:theme="@style/AppTheme.Translucent" />
|
||||||
|
<activity
|
||||||
|
android:name=".superuser.RequestActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity="internal.superuser"
|
||||||
|
android:theme="@style/SuRequest" />
|
||||||
|
|
||||||
|
<receiver android:name=".superuser.SuReceiver" />
|
||||||
|
<receiver android:name=".receivers.BootReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name=".receivers.PackageReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
|
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||||
|
<data android:scheme="package" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name=".receivers.ManagerUpdate" />
|
||||||
|
<receiver android:name=".receivers.RebootReceiver" />
|
||||||
|
<receiver android:name=".receivers.ShortcutReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<service android:name=".services.OnBootIntentService" />
|
||||||
|
<service
|
||||||
|
android:name=".services.UpdateCheckService"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
|
<!-- Hardcode GMS version -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.version"
|
||||||
|
android:value="7095000" />
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
@@ -14,8 +14,6 @@ import com.topjohnwu.magisk.components.AboutCardRow;
|
|||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
@@ -33,7 +31,7 @@ public class AboutActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return R.style.AppTheme_Transparent_Dark;
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,12 +54,8 @@ public class AboutActivity extends Activity {
|
|||||||
|
|
||||||
appChangelog.removeSummary();
|
appChangelog.removeSummary();
|
||||||
appChangelog.setOnClickListener(v -> {
|
appChangelog.setOnClickListener(v -> {
|
||||||
try {
|
new MarkDownWindow(this, getString(R.string.app_changelog),
|
||||||
InputStream is = getAssets().open("changelog.md");
|
getResources().openRawResource(R.raw.changelog)).exec();
|
||||||
new MarkDownWindow(this, getString(R.string.app_changelog), is).exec();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
String translators = getString(R.string.translators);
|
String translators = getString(R.string.translators);
|
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -17,6 +18,7 @@ import com.topjohnwu.magisk.asyncs.FlashZip;
|
|||||||
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.superuser.CallbackList;
|
import com.topjohnwu.superuser.CallbackList;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ public class FlashActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return R.style.AppTheme_Transparent_Dark;
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,13 +115,18 @@ public class FlashActivity extends Activity {
|
|||||||
case Const.Value.FLASH_ZIP:
|
case Const.Value.FLASH_ZIP:
|
||||||
new FlashZip(this, uri, console, logs).exec();
|
new FlashZip(this, uri, console, logs).exec();
|
||||||
break;
|
break;
|
||||||
case Const.Value.PATCH_BOOT:
|
case Const.Value.UNINSTALL:
|
||||||
new InstallMagisk(this, console, logs, uri, (Uri) intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT))
|
new UninstallMagisk(this, uri, console, logs).exec();
|
||||||
.exec();
|
|
||||||
break;
|
break;
|
||||||
case Const.Value.FLASH_MAGISK:
|
case Const.Value.FLASH_MAGISK:
|
||||||
new InstallMagisk(this, console, logs, uri, intent.getStringExtra(Const.Key.FLASH_SET_BOOT))
|
new InstallMagisk(this, console, logs, uri, InstallMagisk.DIRECT_MODE).exec();
|
||||||
.exec();
|
break;
|
||||||
|
case Const.Value.FLASH_SECOND_SLOT:
|
||||||
|
new InstallMagisk(this, console, logs, uri, InstallMagisk.SECOND_SLOT_MODE).exec();
|
||||||
|
break;
|
||||||
|
case Const.Value.PATCH_BOOT:
|
||||||
|
new InstallMagisk(this, console, logs, uri,
|
||||||
|
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,4 +135,21 @@ public class FlashActivity extends Activity {
|
|||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
// Prevent user accidentally press back button
|
// Prevent user accidentally press back button
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class UninstallMagisk extends FlashZip {
|
||||||
|
|
||||||
|
private UninstallMagisk(Activity context, Uri uri, List<String> console, List<String> logs) {
|
||||||
|
super(context, uri, console, logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Integer result) {
|
||||||
|
if (result == 1) {
|
||||||
|
new Handler().postDelayed(() ->
|
||||||
|
RootUtils.uninstallPkg(getActivity().getPackageName()), 3000);
|
||||||
|
} else {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -7,6 +7,7 @@ import android.support.annotation.Nullable;
|
|||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
import android.support.v7.widget.CardView;
|
import android.support.v7.widget.CardView;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -22,10 +23,13 @@ import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
|||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.Fragment;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
import com.topjohnwu.magisk.utils.ShowUI;
|
import com.topjohnwu.magisk.utils.ShowUI;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
import butterknife.BindColor;
|
import butterknife.BindColor;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
@@ -36,14 +40,6 @@ import butterknife.Unbinder;
|
|||||||
public class MagiskFragment extends Fragment
|
public class MagiskFragment extends Fragment
|
||||||
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
|
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
|
||||||
|
|
||||||
private static final int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
|
||||||
private static final int CAUSE_NETWORK_LOST = 0x02;
|
|
||||||
private static final int RESPONSE_ERR = 0x04;
|
|
||||||
private static final int CONNECTION_FAIL = 0x08;
|
|
||||||
|
|
||||||
private static final int BASIC_PASS = 0x10;
|
|
||||||
private static final int CTS_PASS = 0x20;
|
|
||||||
|
|
||||||
private Container expandableContainer = new Container();
|
private Container expandableContainer = new Container();
|
||||||
|
|
||||||
private MagiskManager mm;
|
private MagiskManager mm;
|
||||||
@@ -91,7 +87,14 @@ public class MagiskFragment extends Fragment
|
|||||||
new CheckSafetyNet(getActivity()).exec();
|
new CheckSafetyNet(getActivity()).exec();
|
||||||
collapse();
|
collapse();
|
||||||
};
|
};
|
||||||
if (!CheckSafetyNet.dexPath.exists()) {
|
if (!TextUtils.equals(mm.getPackageName(), Const.ORIG_PKG_NAME)) {
|
||||||
|
new AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.cannot_check_sn_title)
|
||||||
|
.setMessage(R.string.cannot_check_sn_notice)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
} else if (!CheckSafetyNet.dexPath.exists()) {
|
||||||
// Show dialog
|
// Show dialog
|
||||||
new AlertDialogBuilder(getActivity())
|
new AlertDialogBuilder(getActivity())
|
||||||
.setTitle(R.string.proprietary_title)
|
.setTitle(R.string.proprietary_title)
|
||||||
@@ -159,8 +162,8 @@ public class MagiskFragment extends Fragment
|
|||||||
|
|
||||||
safetyNetStatusText.setText(R.string.safetyNet_check_text);
|
safetyNetStatusText.setText(R.string.safetyNet_check_text);
|
||||||
|
|
||||||
mm.safetyNetDone.hasPublished = false;
|
mm.safetyNetDone.reset();
|
||||||
mm.updateCheckDone.hasPublished = false;
|
mm.updateCheckDone.reset();
|
||||||
mm.remoteMagiskVersionString = null;
|
mm.remoteMagiskVersionString = null;
|
||||||
mm.remoteMagiskVersionCode = -1;
|
mm.remoteMagiskVersionCode = -1;
|
||||||
collapse();
|
collapse();
|
||||||
@@ -176,11 +179,11 @@ public class MagiskFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
if (topic == mm.updateCheckDone) {
|
if (topic == mm.updateCheckDone) {
|
||||||
updateCheckUI();
|
updateCheckUI();
|
||||||
} else if (topic == mm.safetyNetDone) {
|
} else if (topic == mm.safetyNetDone) {
|
||||||
updateSafetyNetUI((int) result);
|
updateSafetyNetUI((int) topic.getResults()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +208,7 @@ public class MagiskFragment extends Fragment
|
|||||||
|
|
||||||
boolean hasNetwork = Utils.checkNetworkStatus();
|
boolean hasNetwork = Utils.checkNetworkStatus();
|
||||||
boolean hasRoot = Shell.rootAccess();
|
boolean hasRoot = Shell.rootAccess();
|
||||||
boolean isUpToDate = mm.magiskVersionCode > 1300;
|
boolean isUpToDate = mm.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
|
||||||
|
|
||||||
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
||||||
safetyNetCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
safetyNetCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
||||||
@@ -250,17 +253,22 @@ public class MagiskFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shownDialog && (mm.remoteMagiskVersionCode > mm.magiskVersionCode
|
|
||||||
|| mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE)) {
|
|
||||||
install();
|
|
||||||
}
|
|
||||||
|
|
||||||
magiskUpdateIcon.setImageResource(image);
|
magiskUpdateIcon.setImageResource(image);
|
||||||
magiskUpdateIcon.setColorFilter(color);
|
magiskUpdateIcon.setColorFilter(color);
|
||||||
magiskUpdateIcon.setVisibility(View.VISIBLE);
|
magiskUpdateIcon.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
magiskUpdateProgress.setVisibility(View.GONE);
|
magiskUpdateProgress.setVisibility(View.GONE);
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
|
||||||
|
if (!shownDialog) {
|
||||||
|
if (mm.remoteMagiskVersionCode > mm.magiskVersionCode
|
||||||
|
|| mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
|
install();
|
||||||
|
} else if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.FIX_ENV &&
|
||||||
|
!ShellUtils.fastCmdResult("env_check")) {
|
||||||
|
ShowUI.envFixDialog(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSafetyNetUI(int response) {
|
private void updateSafetyNetUI(int response) {
|
||||||
@@ -270,12 +278,12 @@ public class MagiskFragment extends Fragment
|
|||||||
safetyNetStatusText.setText(R.string.safetyNet_check_success);
|
safetyNetStatusText.setText(R.string.safetyNet_check_success);
|
||||||
|
|
||||||
boolean b;
|
boolean b;
|
||||||
b = (response & CTS_PASS) != 0;
|
b = (response & ISafetyNetHelper.CTS_PASS) != 0;
|
||||||
ctsStatusText.setText("ctsProfile: " + b);
|
ctsStatusText.setText("ctsProfile: " + b);
|
||||||
ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
||||||
ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
||||||
|
|
||||||
b = (response & BASIC_PASS) != 0;
|
b = (response & ISafetyNetHelper.BASIC_PASS) != 0;
|
||||||
basicStatusText.setText("basicIntegrity: " + b);
|
basicStatusText.setText("basicIntegrity: " + b);
|
||||||
basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
||||||
basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
||||||
@@ -284,16 +292,16 @@ public class MagiskFragment extends Fragment
|
|||||||
} else {
|
} else {
|
||||||
@StringRes int resid;
|
@StringRes int resid;
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case CAUSE_SERVICE_DISCONNECTED:
|
case ISafetyNetHelper.CAUSE_SERVICE_DISCONNECTED:
|
||||||
resid = R.string.safetyNet_network_loss;
|
resid = R.string.safetyNet_network_loss;
|
||||||
break;
|
break;
|
||||||
case CAUSE_NETWORK_LOST:
|
case ISafetyNetHelper.CAUSE_NETWORK_LOST:
|
||||||
resid = R.string.safetyNet_service_disconnected;
|
resid = R.string.safetyNet_service_disconnected;
|
||||||
break;
|
break;
|
||||||
case RESPONSE_ERR:
|
case ISafetyNetHelper.RESPONSE_ERR:
|
||||||
resid = R.string.safetyNet_res_invalid;
|
resid = R.string.safetyNet_res_invalid;
|
||||||
break;
|
break;
|
||||||
case CONNECTION_FAIL:
|
case ISafetyNetHelper.CONNECTION_FAIL:
|
||||||
default:
|
default:
|
||||||
resid = R.string.safetyNet_api_error;
|
resid = R.string.safetyNet_api_error;
|
||||||
break;
|
break;
|
@@ -28,7 +28,6 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
private ApplicationAdapter appAdapter;
|
private ApplicationAdapter appAdapter;
|
||||||
|
|
||||||
private SearchView.OnQueryTextListener searchListener;
|
private SearchView.OnQueryTextListener searchListener;
|
||||||
private String lastFilter;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
@@ -41,25 +40,22 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = ButterKnife.bind(this, view);
|
||||||
lastFilter = "";
|
|
||||||
|
|
||||||
mSwipeRefreshLayout.setRefreshing(true);
|
mSwipeRefreshLayout.setRefreshing(true);
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh());
|
mSwipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh());
|
||||||
|
|
||||||
appAdapter = new ApplicationAdapter(getActivity());
|
appAdapter = new ApplicationAdapter();
|
||||||
recyclerView.setAdapter(appAdapter);
|
recyclerView.setAdapter(appAdapter);
|
||||||
|
|
||||||
searchListener = new SearchView.OnQueryTextListener() {
|
searchListener = new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
lastFilter = query;
|
|
||||||
appAdapter.filter(query);
|
appAdapter.filter(query);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextChange(String newText) {
|
public boolean onQueryTextChange(String newText) {
|
||||||
lastFilter = newText;
|
|
||||||
appAdapter.filter(newText);
|
appAdapter.filter(newText);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -84,9 +80,9 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
appAdapter.filter(lastFilter);
|
appAdapter.filter(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
145
src/full/java/com/topjohnwu/magisk/MagiskLogFragment.java
Normal file
145
src/full/java/com/topjohnwu/magisk/MagiskLogFragment.java
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.Fragment;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.Unbinder;
|
||||||
|
|
||||||
|
public class MagiskLogFragment extends Fragment {
|
||||||
|
|
||||||
|
private Unbinder unbinder;
|
||||||
|
|
||||||
|
@BindView(R.id.txtLog) TextView txtLog;
|
||||||
|
@BindView(R.id.svLog) ScrollView svLog;
|
||||||
|
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
|
||||||
|
@BindView(R.id.progressBar) ProgressBar progressBar;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
|
||||||
|
unbinder = ButterKnife.bind(this, view);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
txtLog.setTextIsSelectable(true);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
getActivity().setTitle(R.string.log);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
readLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
unbinder.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.menu_log, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_refresh:
|
||||||
|
readLogs();
|
||||||
|
return true;
|
||||||
|
case R.id.menu_save:
|
||||||
|
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, this::saveLogs);
|
||||||
|
return true;
|
||||||
|
case R.id.menu_clear:
|
||||||
|
clearLogs();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readLogs() {
|
||||||
|
Shell.Async.su(new Shell.Async.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
if (ShellUtils.isValidOutput(out)) {
|
||||||
|
txtLog.setText(TextUtils.join("\n", out));
|
||||||
|
} else {
|
||||||
|
txtLog.setText(R.string.log_is_empty);
|
||||||
|
}
|
||||||
|
svLog.postDelayed(() -> svLog.fullScroll(ScrollView.FOCUS_DOWN), 100);
|
||||||
|
hsvLog.postDelayed(() -> hsvLog.fullScroll(ScrollView.FOCUS_LEFT), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskError(@NonNull Throwable throwable) {
|
||||||
|
txtLog.setText(R.string.log_is_empty);
|
||||||
|
}
|
||||||
|
}, "cat " + Const.MAGISK_LOG + " | tail -n 5000");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveLogs() {
|
||||||
|
Calendar now = Calendar.getInstance();
|
||||||
|
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
|
||||||
|
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||||
|
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||||
|
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||||
|
|
||||||
|
File targetFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
|
||||||
|
targetFile.getParentFile().mkdirs();
|
||||||
|
try {
|
||||||
|
targetFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Shell.Async.su(new Shell.Async.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
|
||||||
|
SnackbarMaker.make(txtLog, targetFile.getPath(), Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskError(@NonNull Throwable throwable) {}
|
||||||
|
}, "cat " + Const.MAGISK_LOG + " > " + targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearLogs() {
|
||||||
|
Shell.Async.su("echo -n > " + Const.MAGISK_LOG);
|
||||||
|
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
292
src/full/java/com/topjohnwu/magisk/MagiskManager.java
Normal file
292
src/full/java/com/topjohnwu/magisk/MagiskManager.java
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.app.job.JobInfo;
|
||||||
|
import android.app.job.JobScheduler;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.Application;
|
||||||
|
import com.topjohnwu.magisk.container.Module;
|
||||||
|
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
||||||
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
|
import com.topjohnwu.magisk.utils.ShellInitializer;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MagiskManager extends Application implements Shell.Container {
|
||||||
|
|
||||||
|
// Topics
|
||||||
|
public final Topic magiskHideDone = new Topic();
|
||||||
|
public final Topic reloadActivity = new Topic();
|
||||||
|
public final Topic moduleLoadDone = new Topic();
|
||||||
|
public final Topic repoLoadDone = new Topic();
|
||||||
|
public final Topic updateCheckDone = new Topic();
|
||||||
|
public final Topic safetyNetDone = new Topic();
|
||||||
|
public final Topic localeDone = new Topic();
|
||||||
|
|
||||||
|
// Info
|
||||||
|
public boolean hasInit = false;
|
||||||
|
public String magiskVersionString;
|
||||||
|
public int magiskVersionCode = -1;
|
||||||
|
public String remoteMagiskVersionString;
|
||||||
|
public int remoteMagiskVersionCode = -1;
|
||||||
|
public String remoteManagerVersionString;
|
||||||
|
public int remoteManagerVersionCode = -1;
|
||||||
|
|
||||||
|
public String magiskLink;
|
||||||
|
public String magiskNoteLink;
|
||||||
|
public String managerLink;
|
||||||
|
public String managerNoteLink;
|
||||||
|
public String uninstallerLink;
|
||||||
|
|
||||||
|
public boolean keepVerity = false;
|
||||||
|
public boolean keepEnc = false;
|
||||||
|
|
||||||
|
// Data
|
||||||
|
public Map<String, Module> moduleMap;
|
||||||
|
public List<Locale> locales;
|
||||||
|
|
||||||
|
public boolean magiskHide;
|
||||||
|
public boolean isDarkTheme;
|
||||||
|
public int suRequestTimeout;
|
||||||
|
public int suLogTimeout = 14;
|
||||||
|
public int suAccessState;
|
||||||
|
public int multiuserMode;
|
||||||
|
public int suResponseType;
|
||||||
|
public int suNotificationType;
|
||||||
|
public int suNamespaceMode;
|
||||||
|
public String localeConfig;
|
||||||
|
public int updateChannel;
|
||||||
|
public String bootFormat;
|
||||||
|
public int repoOrder;
|
||||||
|
|
||||||
|
// Global resources
|
||||||
|
public SharedPreferences prefs;
|
||||||
|
public MagiskDatabaseHelper mDB;
|
||||||
|
public RepoDatabaseHelper repoDB;
|
||||||
|
|
||||||
|
private volatile Shell mShell;
|
||||||
|
|
||||||
|
public MagiskManager() {
|
||||||
|
weakSelf = new WeakReference<>(this);
|
||||||
|
Shell.setContainer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Shell getShell() {
|
||||||
|
return mShell;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShell(@Nullable Shell shell) {
|
||||||
|
mShell = shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
Shell.setFlags(Shell.FLAG_MOUNT_MASTER);
|
||||||
|
Shell.verboseLogging(BuildConfig.DEBUG);
|
||||||
|
Shell.setInitializer(ShellInitializer.class);
|
||||||
|
|
||||||
|
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
mDB = MagiskDatabaseHelper.getInstance(this);
|
||||||
|
|
||||||
|
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
|
||||||
|
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
|
mDB.setStrings(Const.Key.SU_MANAGER, null);
|
||||||
|
RootUtils.uninstallPkg(pkg);
|
||||||
|
}
|
||||||
|
if (TextUtils.equals(pkg, getPackageName())) {
|
||||||
|
try {
|
||||||
|
// We are the manager, remove com.topjohnwu.magisk as it could be malware
|
||||||
|
getPackageManager().getApplicationInfo(Const.ORIG_PKG_NAME, 0);
|
||||||
|
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocale();
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MagiskManager get() {
|
||||||
|
return (MagiskManager) weakSelf.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocale() {
|
||||||
|
localeConfig = prefs.getString(Const.Key.LOCALE, "");
|
||||||
|
if (localeConfig.isEmpty()) {
|
||||||
|
locale = defaultLocale;
|
||||||
|
} else {
|
||||||
|
locale = Locale.forLanguageTag(localeConfig);
|
||||||
|
}
|
||||||
|
Resources res = getBaseContext().getResources();
|
||||||
|
Configuration config = new Configuration(res.getConfiguration());
|
||||||
|
config.setLocale(locale);
|
||||||
|
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadConfig() {
|
||||||
|
// su
|
||||||
|
suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
|
||||||
|
suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
|
||||||
|
suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
|
||||||
|
suAccessState = mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
|
||||||
|
multiuserMode = mDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
|
||||||
|
suNamespaceMode = mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
|
||||||
|
|
||||||
|
// config
|
||||||
|
isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
|
||||||
|
updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
|
||||||
|
bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
||||||
|
repoOrder = prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeConfig() {
|
||||||
|
prefs.edit()
|
||||||
|
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
||||||
|
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
||||||
|
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
|
||||||
|
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
||||||
|
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
||||||
|
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
||||||
|
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
|
||||||
|
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
|
||||||
|
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
|
||||||
|
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
|
||||||
|
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
|
||||||
|
.putString(Const.Key.LOCALE, localeConfig)
|
||||||
|
.putString(Const.Key.BOOT_FORMAT, bootFormat)
|
||||||
|
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
||||||
|
.putInt(Const.Key.REPO_ORDER, repoOrder)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMagiskInfo() {
|
||||||
|
try {
|
||||||
|
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
||||||
|
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
||||||
|
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
|
||||||
|
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
|
||||||
|
magiskHide = s == null || Integer.parseInt(s) != 0;
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getDefaultInstallFlags() {
|
||||||
|
keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
||||||
|
keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupUpdateCheck() {
|
||||||
|
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
|
||||||
|
|
||||||
|
if (prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
|
||||||
|
if (scheduler.getAllPendingJobs().isEmpty() ||
|
||||||
|
Const.UPDATE_SERVICE_VER > prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
||||||
|
ComponentName service = new ComponentName(this, UpdateCheckService.class);
|
||||||
|
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
||||||
|
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||||
|
.setPersisted(true)
|
||||||
|
.setPeriodic(8 * 60 * 60 * 1000)
|
||||||
|
.build();
|
||||||
|
scheduler.schedule(info);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scheduler.cancel(Const.UPDATE_SERVICE_VER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dumpPrefs() {
|
||||||
|
// Flush prefs to disk
|
||||||
|
prefs.edit().commit();
|
||||||
|
File xml = new File(getFilesDir().getParent() + "/shared_prefs",
|
||||||
|
getPackageName() + "_preferences.xml");
|
||||||
|
Shell.Sync.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadPrefs() {
|
||||||
|
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
|
||||||
|
if (config.exists()) {
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
try {
|
||||||
|
SuFileInputStream is = new SuFileInputStream(config);
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||||
|
parser.setInput(is, "UTF-8");
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "map");
|
||||||
|
while (parser.next() != XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getEventType() != XmlPullParser.START_TAG)
|
||||||
|
continue;
|
||||||
|
String key = parser.getAttributeValue(null, "name");
|
||||||
|
String value = parser.getAttributeValue(null, "value");
|
||||||
|
switch (parser.getName()) {
|
||||||
|
case "string":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "string");
|
||||||
|
editor.putString(key, parser.nextText());
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "string");
|
||||||
|
break;
|
||||||
|
case "boolean":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "boolean");
|
||||||
|
editor.putBoolean(key, Boolean.parseBoolean(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "boolean");
|
||||||
|
break;
|
||||||
|
case "int":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int");
|
||||||
|
editor.putInt(key, Integer.parseInt(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int");
|
||||||
|
break;
|
||||||
|
case "long":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "long");
|
||||||
|
editor.putLong(key, Long.parseLong(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "long");
|
||||||
|
break;
|
||||||
|
case "float":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int");
|
||||||
|
editor.putFloat(key, Float.parseFloat(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
editor.remove(Const.Key.ETAG_KEY);
|
||||||
|
editor.apply();
|
||||||
|
loadConfig();
|
||||||
|
config.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
@@ -16,16 +15,12 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
@@ -33,8 +28,8 @@ public class MainActivity extends Activity
|
|||||||
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
|
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
|
||||||
|
|
||||||
private final Handler mDrawerHandler = new Handler();
|
private final Handler mDrawerHandler = new Handler();
|
||||||
private SharedPreferences prefs;
|
|
||||||
private int mDrawerItem;
|
private int mDrawerItem;
|
||||||
|
private boolean fromShortcut = true;
|
||||||
|
|
||||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||||
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
||||||
@@ -51,7 +46,6 @@ public class MainActivity extends Activity
|
|||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
|
||||||
MagiskManager mm = getMagiskManager();
|
MagiskManager mm = getMagiskManager();
|
||||||
prefs = mm.prefs;
|
|
||||||
|
|
||||||
if (!mm.hasInit) {
|
if (!mm.hasInit) {
|
||||||
Intent intent = new Intent(this, SplashActivity.class);
|
Intent intent = new Intent(this, SplashActivity.class);
|
||||||
@@ -96,16 +90,6 @@ public class MainActivity extends Activity
|
|||||||
navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
||||||
|
|
||||||
navigationView.setNavigationItemSelectedListener(this);
|
navigationView.setNavigationItemSelectedListener(this);
|
||||||
|
|
||||||
if (mm.prefs.getInt(Const.Key.APP_VER, -1) < BuildConfig.VERSION_CODE) {
|
|
||||||
prefs.edit().putInt(Const.Key.APP_VER, BuildConfig.VERSION_CODE).apply();
|
|
||||||
try {
|
|
||||||
InputStream is = getAssets().open("changelog.md");
|
|
||||||
new MarkDownWindow(this, getString(R.string.app_changelog), is).exec();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -118,7 +102,7 @@ public class MainActivity extends Activity
|
|||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (drawer.isDrawerOpen(navigationView)) {
|
if (drawer.isDrawerOpen(navigationView)) {
|
||||||
drawer.closeDrawer(navigationView);
|
drawer.closeDrawer(navigationView);
|
||||||
} else if (mDrawerItem != R.id.magisk) {
|
} else if (mDrawerItem != R.id.magisk && !fromShortcut) {
|
||||||
navigate(R.id.magisk);
|
navigate(R.id.magisk);
|
||||||
} else {
|
} else {
|
||||||
finish();
|
finish();
|
||||||
@@ -134,7 +118,7 @@ public class MainActivity extends Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
recreate();
|
recreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,8 +131,8 @@ public class MainActivity extends Activity
|
|||||||
MagiskManager mm = getMagiskManager();
|
MagiskManager mm = getMagiskManager();
|
||||||
Menu menu = navigationView.getMenu();
|
Menu menu = navigationView.getMenu();
|
||||||
menu.findItem(R.id.magiskhide).setVisible(
|
menu.findItem(R.id.magiskhide).setVisible(
|
||||||
Shell.rootAccess() && mm.magiskVersionCode >= 1300
|
Shell.rootAccess() && mm.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
|
||||||
&& prefs.getBoolean(Const.Key.MAGISKHIDE, false));
|
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
|
||||||
menu.findItem(R.id.modules).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false) &&
|
menu.findItem(R.id.modules).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false) &&
|
||||||
Shell.rootAccess() && mm.magiskVersionCode >= 0);
|
Shell.rootAccess() && mm.magiskVersionCode >= 0);
|
||||||
menu.findItem(R.id.downloads).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false)
|
menu.findItem(R.id.downloads).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false)
|
||||||
@@ -162,9 +146,6 @@ public class MainActivity extends Activity
|
|||||||
int itemId = R.id.magisk;
|
int itemId = R.id.magisk;
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
switch (item) {
|
switch (item) {
|
||||||
case "magisk":
|
|
||||||
itemId = R.id.magisk;
|
|
||||||
break;
|
|
||||||
case "superuser":
|
case "superuser":
|
||||||
itemId = R.id.superuser;
|
itemId = R.id.superuser;
|
||||||
break;
|
break;
|
||||||
@@ -197,22 +178,23 @@ public class MainActivity extends Activity
|
|||||||
navigationView.setCheckedItem(itemId);
|
navigationView.setCheckedItem(itemId);
|
||||||
switch (itemId) {
|
switch (itemId) {
|
||||||
case R.id.magisk:
|
case R.id.magisk:
|
||||||
displayFragment(new MagiskFragment(), "magisk", true);
|
fromShortcut = false;
|
||||||
|
displayFragment(new MagiskFragment(), true);
|
||||||
break;
|
break;
|
||||||
case R.id.superuser:
|
case R.id.superuser:
|
||||||
displayFragment(new SuperuserFragment(), "superuser", true);
|
displayFragment(new SuperuserFragment(), true);
|
||||||
break;
|
break;
|
||||||
case R.id.modules:
|
case R.id.modules:
|
||||||
displayFragment(new ModulesFragment(), "modules", true);
|
displayFragment(new ModulesFragment(), true);
|
||||||
break;
|
break;
|
||||||
case R.id.downloads:
|
case R.id.downloads:
|
||||||
displayFragment(new ReposFragment(), "downloads", true);
|
displayFragment(new ReposFragment(), true);
|
||||||
break;
|
break;
|
||||||
case R.id.magiskhide:
|
case R.id.magiskhide:
|
||||||
displayFragment(new MagiskHideFragment(), Const.Key.MAGISKHIDE, true);
|
displayFragment(new MagiskHideFragment(), true);
|
||||||
break;
|
break;
|
||||||
case R.id.log:
|
case R.id.log:
|
||||||
displayFragment(new LogFragment(), "log", false);
|
displayFragment(new LogFragment(), false);
|
||||||
break;
|
break;
|
||||||
case R.id.settings:
|
case R.id.settings:
|
||||||
startActivity(new Intent(this, SettingsActivity.class));
|
startActivity(new Intent(this, SettingsActivity.class));
|
||||||
@@ -225,12 +207,13 @@ public class MainActivity extends Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayFragment(@NonNull Fragment navFragment, String tag, boolean setElevation) {
|
private void displayFragment(@NonNull Fragment navFragment, boolean setElevation) {
|
||||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
|
||||||
supportInvalidateOptionsMenu();
|
supportInvalidateOptionsMenu();
|
||||||
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
|
getSupportFragmentManager()
|
||||||
transaction.replace(R.id.content_frame, navFragment, tag).commitNow();
|
.beginTransaction()
|
||||||
if (setElevation) toolbar.setElevation(toolbarElevation);
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
else toolbar.setElevation(0);
|
.replace(R.id.content_frame, navFragment)
|
||||||
|
.commitNow();
|
||||||
|
toolbar.setElevation(setElevation ? toolbarElevation : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -21,7 +21,6 @@ import com.topjohnwu.magisk.components.Fragment;
|
|||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -40,7 +39,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
|
|||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
@OnClick(R.id.fab)
|
@OnClick(R.id.fab)
|
||||||
public void selectFile() {
|
public void selectFile() {
|
||||||
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
|
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.setType("application/zip");
|
intent.setType("application/zip");
|
||||||
startActivityForResult(intent, Const.ID.FETCH_ZIP);
|
startActivityForResult(intent, Const.ID.FETCH_ZIP);
|
||||||
@@ -79,7 +78,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
|
|
26
src/full/java/com/topjohnwu/magisk/NoUIActivity.java
Normal file
26
src/full/java/com/topjohnwu/magisk/NoUIActivity.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
|
||||||
|
public class NoUIActivity extends Activity {
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
String[] perms = getIntent().getStringArrayExtra(Const.Key.INTENT_PERM);
|
||||||
|
if (perms != null) {
|
||||||
|
ActivityCompat.requestPermissions(this, perms, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
@@ -27,6 +27,7 @@ import butterknife.Unbinder;
|
|||||||
public class ReposFragment extends Fragment implements Topic.Subscriber {
|
public class ReposFragment extends Fragment implements Topic.Subscriber {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
private Unbinder unbinder;
|
||||||
|
private MagiskManager mm;
|
||||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||||
@@ -44,8 +45,9 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_repos, container, false);
|
View view = inflater.inflate(R.layout.fragment_repos, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = ButterKnife.bind(this, view);
|
||||||
|
mm = getApplication();
|
||||||
|
|
||||||
mSwipeRefreshLayout.setRefreshing(true);
|
mSwipeRefreshLayout.setRefreshing(mm.repoLoadDone.isPending());
|
||||||
|
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
recyclerView.setVisibility(View.VISIBLE);
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
@@ -60,7 +62,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
adapter = new ReposAdapter(getApplication().repoDB, getApplication().moduleMap);
|
adapter = new ReposAdapter(mm.repoDB, mm.moduleMap);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
super.onResume();
|
super.onResume();
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
|
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
|
||||||
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
@@ -80,7 +82,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Topic[] getSubscription() {
|
public Topic[] getSubscription() {
|
||||||
return new Topic[] { getApplication().repoLoadDone };
|
return new Topic[] { mm.repoLoadDone };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -103,7 +105,6 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
MagiskManager mm = getApplication();
|
|
||||||
if (item.getItemId() == R.id.repo_sort) {
|
if (item.getItemId() == R.id.repo_sort) {
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setTitle(R.string.sorting_order)
|
.setTitle(R.string.sorting_order)
|
@@ -1,33 +1,36 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.ListPreference;
|
import android.support.v14.preference.SwitchPreference;
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.PreferenceCategory;
|
|
||||||
import android.preference.PreferenceFragment;
|
|
||||||
import android.preference.PreferenceScreen;
|
|
||||||
import android.preference.SwitchPreference;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.preference.ListPreference;
|
||||||
|
import android.support.v7.preference.Preference;
|
||||||
|
import android.support.v7.preference.PreferenceCategory;
|
||||||
|
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||||
|
import android.support.v7.preference.PreferenceScreen;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.asyncs.HideManager;
|
import com.topjohnwu.magisk.asyncs.HideManager;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -41,7 +44,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return R.style.AppTheme_Transparent_Dark;
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,13 +66,13 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
setFloating();
|
setFloating();
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
getFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
|
getSupportFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
recreate();
|
recreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +81,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
return new Topic[] { getMagiskManager().reloadActivity };
|
return new Topic[] { getMagiskManager().reloadActivity };
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragment
|
public static class SettingsFragment extends PreferenceFragmentCompat
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
|
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
|
||||||
|
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
@@ -90,9 +93,8 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
private PreferenceCategory generalCatagory;
|
private PreferenceCategory generalCatagory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
super.onCreate(savedInstanceState);
|
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
||||||
addPreferencesFromResource(R.xml.app_settings);
|
|
||||||
mm = Utils.getMagiskManager(getActivity());
|
mm = Utils.getMagiskManager(getActivity());
|
||||||
prefs = mm.prefs;
|
prefs = mm.prefs;
|
||||||
prefScreen = getPreferenceScreen();
|
prefScreen = getPreferenceScreen();
|
||||||
@@ -126,13 +128,13 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
EditText url = v.findViewById(R.id.custom_url);
|
EditText url = v.findViewById(R.id.custom_url);
|
||||||
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setTitle(R.string.settings_update_custom)
|
.setTitle(R.string.settings_update_custom)
|
||||||
.setView(v)
|
.setView(v)
|
||||||
.setPositiveButton(R.string.ok, (d, i) ->
|
.setPositiveButton(R.string.ok, (d, i) ->
|
||||||
prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
|
prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
|
||||||
url.getText().toString()).apply())
|
url.getText().toString()).apply())
|
||||||
.setNegativeButton(R.string.close, null)
|
.setNegativeButton(R.string.close, null)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -150,31 +152,34 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
reauth.setSummary(R.string.android_o_not_support);
|
reauth.setSummary(R.string.android_o_not_support);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove fingerprint option if not possible
|
// Disable fingerprint option if not possible
|
||||||
if (!FingerprintHelper.canUseFingerprint()) {
|
if (!FingerprintHelper.canUseFingerprint()) {
|
||||||
suCategory.removePreference(fingerprint);
|
fingerprint.setEnabled(false);
|
||||||
|
fingerprint.setSummary(R.string.disable_fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm.magiskVersionCode >= 1440) {
|
if (mm.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
|
||||||
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
hideManager.setOnPreferenceClickListener((pref) -> {
|
hideManager.setOnPreferenceClickListener((pref) -> {
|
||||||
Utils.runWithPermission(getActivity(),
|
new HideManager(getActivity()).exec();
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
|
||||||
() -> new HideManager(getActivity()).exec());
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
generalCatagory.removePreference(restoreManager);
|
generalCatagory.removePreference(restoreManager);
|
||||||
} else {
|
} else {
|
||||||
if (Utils.checkNetworkStatus()) {
|
if (Utils.checkNetworkStatus()) {
|
||||||
restoreManager.setOnPreferenceClickListener((pref) -> {
|
restoreManager.setOnPreferenceClickListener((pref) -> {
|
||||||
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
|
Utils.dlAndReceive(
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
getActivity(), new DownloadReceiver() {
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
@Override
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME,
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Utils.fmt("MagiskManager-v%s(%d).apk",
|
mm.dumpPrefs();
|
||||||
mm.remoteManagerVersionString, mm.remoteManagerVersionCode));
|
if (ShellUtils.fastCmdResult("pm install " + uri.getPath()))
|
||||||
mm.sendBroadcast(intent);
|
RootUtils.uninstallPkg(context.getPackageName());
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
mm.managerLink,
|
||||||
|
Utils.fmt("MagiskManager-v%s.apk", mm.remoteManagerVersionString)
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -187,18 +192,6 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
generalCatagory.removePreference(hideManager);
|
generalCatagory.removePreference(hideManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME) && mm.magiskVersionCode >= 1440) {
|
|
||||||
hideManager.setOnPreferenceClickListener((pref) -> {
|
|
||||||
Utils.runWithPermission(getActivity(),
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
|
||||||
() -> new HideManager(getActivity()).exec());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
generalCatagory.removePreference(restoreManager);
|
|
||||||
} else {
|
|
||||||
generalCatagory.removePreference(hideManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
|
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
|
||||||
mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
||||||
prefScreen.removePreference(suCategory);
|
prefScreen.removePreference(suCategory);
|
||||||
@@ -207,19 +200,15 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess()) {
|
||||||
prefScreen.removePreference(magiskCategory);
|
prefScreen.removePreference(magiskCategory);
|
||||||
generalCatagory.removePreference(hideManager);
|
generalCatagory.removePreference(hideManager);
|
||||||
} else if (mm.magiskVersionCode < 1300) {
|
} else if (mm.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
|
||||||
prefScreen.removePreference(magiskCategory);
|
prefScreen.removePreference(magiskCategory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLocalePreference(ListPreference lp) {
|
private void setLocalePreference(ListPreference lp) {
|
||||||
boolean isNew = lp == null;
|
|
||||||
if (isNew) {
|
|
||||||
lp = new ListPreference(getActivity());
|
|
||||||
}
|
|
||||||
CharSequence[] entries = new CharSequence[mm.locales.size() + 1];
|
CharSequence[] entries = new CharSequence[mm.locales.size() + 1];
|
||||||
CharSequence[] entryValues = new CharSequence[mm.locales.size() + 1];
|
CharSequence[] entryValues = new CharSequence[mm.locales.size() + 1];
|
||||||
entries[0] = getString(R.string.system_default);
|
entries[0] = Utils.getLocaleString(MagiskManager.defaultLocale, R.string.system_default);
|
||||||
entryValues[0] = "";
|
entryValues[0] = "";
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (Locale locale : mm.locales) {
|
for (Locale locale : mm.locales) {
|
||||||
@@ -228,26 +217,21 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
lp.setEntries(entries);
|
lp.setEntries(entries);
|
||||||
lp.setEntryValues(entryValues);
|
lp.setEntryValues(entryValues);
|
||||||
lp.setTitle(R.string.language);
|
|
||||||
lp.setKey(Const.Key.LOCALE);
|
|
||||||
lp.setSummary(MagiskManager.locale.getDisplayName(MagiskManager.locale));
|
lp.setSummary(MagiskManager.locale.getDisplayName(MagiskManager.locale));
|
||||||
if (isNew) {
|
|
||||||
generalCatagory.addPreference(lp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
super.onResume();
|
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
subscribeTopics();
|
subscribeTopics();
|
||||||
|
return super.onCreateView(inflater, container, savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onDestroyView() {
|
||||||
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
unsubscribeTopics();
|
unsubscribeTopics();
|
||||||
super.onPause();
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -257,7 +241,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
case Const.Key.DARK_THEME:
|
case Const.Key.DARK_THEME:
|
||||||
mm.isDarkTheme = prefs.getBoolean(key, false);
|
mm.isDarkTheme = prefs.getBoolean(key, false);
|
||||||
mm.reloadActivity.publish(false);
|
mm.reloadActivity.publish(false);
|
||||||
break;
|
return;
|
||||||
case Const.Key.COREONLY:
|
case Const.Key.COREONLY:
|
||||||
if (prefs.getBoolean(key, false)) {
|
if (prefs.getBoolean(key, false)) {
|
||||||
try {
|
try {
|
||||||
@@ -278,18 +262,18 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
case Const.Key.HOSTS:
|
case Const.Key.HOSTS:
|
||||||
if (prefs.getBoolean(key, false)) {
|
if (prefs.getBoolean(key, false)) {
|
||||||
Shell.Async.su(
|
Shell.Async.su(
|
||||||
"cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE(),
|
"cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE,
|
||||||
"mount -o bind " + Const.MAGISK_HOST_FILE() + " /system/etc/hosts");
|
"mount -o bind " + Const.MAGISK_HOST_FILE + " /system/etc/hosts");
|
||||||
} else {
|
} else {
|
||||||
Shell.Async.su(
|
Shell.Async.su(
|
||||||
"umount -l /system/etc/hosts",
|
"umount -l /system/etc/hosts",
|
||||||
"rm -f " + Const.MAGISK_HOST_FILE());
|
"rm -f " + Const.MAGISK_HOST_FILE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Const.Key.ROOT_ACCESS:
|
case Const.Key.ROOT_ACCESS:
|
||||||
case Const.Key.SU_MULTIUSER_MODE:
|
case Const.Key.SU_MULTIUSER_MODE:
|
||||||
case Const.Key.SU_MNT_NS:
|
case Const.Key.SU_MNT_NS:
|
||||||
mm.suDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
||||||
break;
|
break;
|
||||||
case Const.Key.LOCALE:
|
case Const.Key.LOCALE:
|
||||||
mm.setLocale();
|
mm.setLocale();
|
||||||
@@ -298,6 +282,9 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
case Const.Key.UPDATE_CHANNEL:
|
case Const.Key.UPDATE_CHANNEL:
|
||||||
new CheckUpdates().exec();
|
new CheckUpdates().exec();
|
||||||
break;
|
break;
|
||||||
|
case Const.Key.CHECK_UPDATES:
|
||||||
|
mm.setupUpdateCheck();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
mm.loadConfig();
|
mm.loadConfig();
|
||||||
setSummary();
|
setSummary();
|
||||||
@@ -321,7 +308,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic, Object result) {
|
public void onTopicPublished(Topic topic) {
|
||||||
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
|
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
|
||||||
}
|
}
|
||||||
|
|
@@ -2,10 +2,6 @@ package com.topjohnwu.magisk;
|
|||||||
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.job.JobInfo;
|
|
||||||
import android.app.job.JobScheduler;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -15,27 +11,26 @@ import com.topjohnwu.magisk.asyncs.LoadModules;
|
|||||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
||||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.services.UpdateCheckService;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
public class SplashActivity extends Activity {
|
public class SplashActivity extends Activity {
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDarkTheme() {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
RootUtils.init();
|
||||||
MagiskManager mm = getMagiskManager();
|
MagiskManager mm = getMagiskManager();
|
||||||
|
|
||||||
|
mm.repoDB = new RepoDatabaseHelper(this);
|
||||||
mm.loadMagiskInfo();
|
mm.loadMagiskInfo();
|
||||||
mm.getDefaultInstallFlags();
|
mm.getDefaultInstallFlags();
|
||||||
Utils.loadPrefs();
|
mm.loadPrefs();
|
||||||
|
|
||||||
// Dynamic detect all locales
|
// Dynamic detect all locales
|
||||||
new LoadLocale().exec();
|
new LoadLocale().exec();
|
||||||
@@ -47,36 +42,24 @@ public class SplashActivity extends Activity {
|
|||||||
getSystemService(NotificationManager.class).createNotificationChannel(channel);
|
getSystemService(NotificationManager.class).createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup shortcuts
|
||||||
|
sendBroadcast(new Intent(this, ShortcutReceiver.class));
|
||||||
|
|
||||||
LoadModules loadModuleTask = new LoadModules();
|
LoadModules loadModuleTask = new LoadModules();
|
||||||
|
|
||||||
if (Utils.checkNetworkStatus()) {
|
if (Utils.checkNetworkStatus()) {
|
||||||
|
|
||||||
// Fire update check
|
// Fire update check
|
||||||
new CheckUpdates().exec();
|
new CheckUpdates().exec();
|
||||||
|
|
||||||
// Add repo update check
|
// Add repo update check
|
||||||
loadModuleTask.setCallBack(() -> new UpdateRepos(false).exec());
|
loadModuleTask.setCallBack(() -> new UpdateRepos(false).exec());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Magisk working as expected
|
// Magisk working as expected
|
||||||
if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
|
if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
|
||||||
|
// Update check service
|
||||||
// Add update checking service
|
mm.setupUpdateCheck();
|
||||||
if (Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
|
||||||
ComponentName service = new ComponentName(this, UpdateCheckService.class);
|
|
||||||
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
|
||||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
|
||||||
.setPersisted(true)
|
|
||||||
.setPeriodic(8 * 60 * 60 * 1000)
|
|
||||||
.build();
|
|
||||||
((JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire asynctasks
|
// Fire asynctasks
|
||||||
loadModuleTask.exec();
|
loadModuleTask.exec();
|
||||||
|
|
||||||
// Check dtbo status
|
|
||||||
Utils.patchDTBO();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write back default values
|
// Write back default values
|
@@ -46,7 +46,7 @@ public class SuLogFragment extends Fragment {
|
|||||||
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
|
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
|
||||||
unbinder = ButterKnife.bind(this, v);
|
unbinder = ButterKnife.bind(this, v);
|
||||||
mm = getApplication();
|
mm = getApplication();
|
||||||
adapter = new SuLogAdapter(mm.suDB);
|
adapter = new SuLogAdapter(mm.mDB);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
updateList();
|
updateList();
|
||||||
@@ -73,7 +73,7 @@ public class SuLogFragment extends Fragment {
|
|||||||
updateList();
|
updateList();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_clear:
|
case R.id.menu_clear:
|
||||||
mm.suDB.clearLogs();
|
mm.mDB.clearLogs();
|
||||||
updateList();
|
updateList();
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
@@ -34,13 +34,13 @@ public class SuperuserFragment extends Fragment {
|
|||||||
PackageManager pm = getActivity().getPackageManager();
|
PackageManager pm = getActivity().getPackageManager();
|
||||||
MagiskManager mm = getApplication();
|
MagiskManager mm = getApplication();
|
||||||
|
|
||||||
List<Policy> policyList = mm.suDB.getPolicyList(pm);
|
List<Policy> policyList = mm.mDB.getPolicyList(pm);
|
||||||
|
|
||||||
if (policyList.size() == 0) {
|
if (policyList.size() == 0) {
|
||||||
emptyRv.setVisibility(View.VISIBLE);
|
emptyRv.setVisibility(View.VISIBLE);
|
||||||
recyclerView.setVisibility(View.GONE);
|
recyclerView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
recyclerView.setAdapter(new PolicyAdapter(policyList, mm.suDB, pm));
|
recyclerView.setAdapter(new PolicyAdapter(policyList, mm.mDB, pm));
|
||||||
emptyRv.setVisibility(View.GONE);
|
emptyRv.setVisibility(View.GONE);
|
||||||
recyclerView.setVisibility(View.VISIBLE);
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
@@ -1,9 +1,7 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -14,12 +12,10 @@ import android.widget.Filter;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -32,25 +28,19 @@ import butterknife.ButterKnife;
|
|||||||
|
|
||||||
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
|
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
|
||||||
|
|
||||||
private List<ApplicationInfo> mOriginalList, mList;
|
private List<ApplicationInfo> fullList, showList;
|
||||||
private List<String> mHideList;
|
private List<String> hideList;
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private ApplicationFilter filter;
|
private ApplicationFilter filter;
|
||||||
private Topic magiskHideDone;
|
|
||||||
|
|
||||||
public ApplicationAdapter(Context context) {
|
public ApplicationAdapter() {
|
||||||
mOriginalList = mList = Collections.emptyList();
|
fullList = showList = Collections.emptyList();
|
||||||
mHideList = Collections.emptyList();
|
hideList = Collections.emptyList();
|
||||||
filter = new ApplicationFilter();
|
filter = new ApplicationFilter();
|
||||||
pm = context.getPackageManager();
|
pm = MagiskManager.get().getPackageManager();
|
||||||
magiskHideDone = Utils.getMagiskManager(context).magiskHideDone;
|
|
||||||
new LoadApps().exec();
|
new LoadApps().exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
|
|
||||||
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
|
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
|
||||||
@@ -59,41 +49,28 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||||
ApplicationInfo info = mList.get(position);
|
ApplicationInfo info = showList.get(position);
|
||||||
|
|
||||||
holder.appIcon.setImageDrawable(info.loadIcon(pm));
|
holder.appIcon.setImageDrawable(info.loadIcon(pm));
|
||||||
holder.appName.setText(info.loadLabel(pm));
|
holder.appName.setText(info.loadLabel(pm));
|
||||||
holder.appPackage.setText(info.packageName);
|
holder.appPackage.setText(info.packageName);
|
||||||
|
|
||||||
// Remove all listeners
|
|
||||||
holder.itemView.setOnClickListener(null);
|
|
||||||
holder.checkBox.setOnCheckedChangeListener(null);
|
holder.checkBox.setOnCheckedChangeListener(null);
|
||||||
|
holder.checkBox.setChecked(hideList.contains(info.packageName));
|
||||||
if (Const.SN_DEFAULTLIST.contains(info.packageName)) {
|
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
||||||
holder.checkBox.setChecked(true);
|
if (isChecked) {
|
||||||
holder.checkBox.setEnabled(false);
|
Shell.Async.su("magiskhide --add " + info.packageName);
|
||||||
holder.itemView.setOnClickListener(v ->
|
hideList.add(info.packageName);
|
||||||
SnackbarMaker.make(holder.itemView,
|
} else {
|
||||||
R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).show()
|
Shell.Async.su("magiskhide --rm " + info.packageName);
|
||||||
);
|
hideList.remove(info.packageName);
|
||||||
} else {
|
}
|
||||||
holder.checkBox.setEnabled(true);
|
});
|
||||||
holder.checkBox.setChecked(mHideList.contains(info.packageName));
|
|
||||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
|
||||||
if (isChecked) {
|
|
||||||
Shell.Async.su("magiskhide --add " + info.packageName);
|
|
||||||
mHideList.add(info.packageName);
|
|
||||||
} else {
|
|
||||||
Shell.Async.su("magiskhide --rm " + info.packageName);
|
|
||||||
mHideList.remove(info.packageName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return mList.size();
|
return showList.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void filter(String constraint) {
|
public void filter(String constraint) {
|
||||||
@@ -119,17 +96,21 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
private class ApplicationFilter extends Filter {
|
private class ApplicationFilter extends Filter {
|
||||||
|
|
||||||
|
private boolean lowercaseContains(String s, CharSequence filter) {
|
||||||
|
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FilterResults performFiltering(CharSequence constraint) {
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
if (constraint == null || constraint.length() == 0) {
|
if (constraint == null || constraint.length() == 0) {
|
||||||
mList = mOriginalList;
|
showList = fullList;
|
||||||
} else {
|
} else {
|
||||||
mList = new ArrayList<>();
|
showList = new ArrayList<>();
|
||||||
String filter = constraint.toString().toLowerCase();
|
String filter = constraint.toString().toLowerCase();
|
||||||
for (ApplicationInfo info : mOriginalList) {
|
for (ApplicationInfo info : fullList) {
|
||||||
if (lowercaseContains(info.loadLabel(pm), filter)
|
if (lowercaseContains(info.loadLabel(pm).toString(), filter)
|
||||||
|| lowercaseContains(info.packageName, filter)) {
|
|| lowercaseContains(info.packageName, filter)) {
|
||||||
mList.add(info);
|
showList.add(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,22 +127,32 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
mOriginalList = pm.getInstalledApplications(0);
|
fullList = pm.getInstalledApplications(0);
|
||||||
for (Iterator<ApplicationInfo> i = mOriginalList.iterator(); i.hasNext(); ) {
|
hideList = Shell.Sync.su("magiskhide --ls");
|
||||||
|
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
|
||||||
ApplicationInfo info = i.next();
|
ApplicationInfo info = i.next();
|
||||||
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
|
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
|
||||||
i.remove();
|
i.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(mOriginalList, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
|
Collections.sort(fullList, (a, b) -> {
|
||||||
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
|
boolean ah = hideList.contains(a.packageName);
|
||||||
mHideList = Shell.Sync.su("magiskhide --ls");
|
boolean bh = hideList.contains(b.packageName);
|
||||||
|
if (ah == bh) {
|
||||||
|
return a.loadLabel(pm).toString().toLowerCase().compareTo(
|
||||||
|
b.loadLabel(pm).toString().toLowerCase());
|
||||||
|
} else if (ah) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void v) {
|
protected void onPostExecute(Void v) {
|
||||||
magiskHideDone.publish(false);
|
MagiskManager.get().magiskHideDone.publish(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -16,7 +16,7 @@ import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
|||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -28,11 +28,11 @@ import butterknife.ButterKnife;
|
|||||||
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
|
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
|
||||||
|
|
||||||
private List<Policy> policyList;
|
private List<Policy> policyList;
|
||||||
private SuDatabaseHelper dbHelper;
|
private MagiskDatabaseHelper dbHelper;
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private Set<Policy> expandList = new HashSet<>();
|
private Set<Policy> expandList = new HashSet<>();
|
||||||
|
|
||||||
public PolicyAdapter(List<Policy> list, SuDatabaseHelper db, PackageManager pm) {
|
public PolicyAdapter(List<Policy> list, MagiskDatabaseHelper db, PackageManager pm) {
|
||||||
policyList = list;
|
policyList = list;
|
||||||
dbHelper = db;
|
dbHelper = db;
|
||||||
this.pm = pm;
|
this.pm = pm;
|
@@ -13,7 +13,7 @@ import android.widget.TextView;
|
|||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.container.SuLogEntry;
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
||||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -27,10 +27,10 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
|
|
||||||
private List<List<Integer>> logEntryList;
|
private List<List<Integer>> logEntryList;
|
||||||
private Set<Integer> itemExpanded, sectionExpanded;
|
private Set<Integer> itemExpanded, sectionExpanded;
|
||||||
private SuDatabaseHelper suDB;
|
private MagiskDatabaseHelper suDB;
|
||||||
private Cursor suLogCursor = null;
|
private Cursor suLogCursor = null;
|
||||||
|
|
||||||
public SuLogAdapter(SuDatabaseHelper db) {
|
public SuLogAdapter(MagiskDatabaseHelper db) {
|
||||||
suDB = db;
|
suDB = db;
|
||||||
logEntryList = Collections.emptyList();
|
logEntryList = Collections.emptyList();
|
||||||
sectionExpanded = new HashSet<>();
|
sectionExpanded = new HashSet<>();
|
@@ -0,0 +1,84 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
|
import dalvik.system.DexClassLoader;
|
||||||
|
|
||||||
|
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
||||||
|
|
||||||
|
public static final File dexPath =
|
||||||
|
new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
|
||||||
|
private ISafetyNetHelper helper;
|
||||||
|
|
||||||
|
public CheckSafetyNet(Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dlSnet() throws Exception {
|
||||||
|
Shell.Sync.sh("rm -rf " + dexPath.getParent());
|
||||||
|
dexPath.getParentFile().mkdir();
|
||||||
|
HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
|
||||||
|
try (
|
||||||
|
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
|
||||||
|
InputStream in = new BufferedInputStream(conn.getInputStream())) {
|
||||||
|
ShellUtils.pump(in, out);
|
||||||
|
} finally {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dyload() throws Exception {
|
||||||
|
DexClassLoader loader = new DexClassLoader(dexPath.getPath(), dexPath.getParent(),
|
||||||
|
null, ISafetyNetHelper.class.getClassLoader());
|
||||||
|
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.SafetyNetHelper");
|
||||||
|
helper = (ISafetyNetHelper) clazz.getConstructors()[0]
|
||||||
|
.newInstance(getActivity(), (ISafetyNetHelper.Callback)
|
||||||
|
code -> MagiskManager.get().safetyNetDone.publish(false, code));
|
||||||
|
if (helper.getVersion() != Const.SNET_VER) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Exception doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
dyload();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// If dynamic load failed, try re-downloading and reload
|
||||||
|
dlSnet();
|
||||||
|
dyload();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Exception e) {
|
||||||
|
if (e == null) {
|
||||||
|
helper.attest();
|
||||||
|
} else {
|
||||||
|
e.printStackTrace();
|
||||||
|
MagiskManager.get().safetyNetDone.publish(false, -1);
|
||||||
|
}
|
||||||
|
super.onPostExecute(e);
|
||||||
|
}
|
||||||
|
}
|
@@ -42,11 +42,14 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
|||||||
mm.remoteMagiskVersionString = magisk.getString("version");
|
mm.remoteMagiskVersionString = magisk.getString("version");
|
||||||
mm.remoteMagiskVersionCode = magisk.getInt("versionCode");
|
mm.remoteMagiskVersionCode = magisk.getInt("versionCode");
|
||||||
mm.magiskLink = magisk.getString("link");
|
mm.magiskLink = magisk.getString("link");
|
||||||
mm.releaseNoteLink = magisk.getString("note");
|
mm.magiskNoteLink = magisk.getString("note");
|
||||||
JSONObject manager = json.getJSONObject("app");
|
JSONObject manager = json.getJSONObject("app");
|
||||||
mm.remoteManagerVersionString = manager.getString("version");
|
mm.remoteManagerVersionString = manager.getString("version");
|
||||||
mm.remoteManagerVersionCode = manager.getInt("versionCode");
|
mm.remoteManagerVersionCode = manager.getInt("versionCode");
|
||||||
mm.managerLink = manager.getString("link");
|
mm.managerLink = manager.getString("link");
|
||||||
|
mm.managerNoteLink = manager.getString("note");
|
||||||
|
JSONObject uninstaller = json.getJSONObject("uninstaller");
|
||||||
|
mm.uninstallerLink = uninstaller.getString("link");
|
||||||
} catch (JSONException ignored) {}
|
} catch (JSONException ignored) {}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -54,7 +57,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void v) {
|
protected void onPostExecute(Void v) {
|
||||||
MagiskManager mm = MagiskManager.get();
|
MagiskManager mm = MagiskManager.get();
|
||||||
if (showNotification && mm.prefs.getBoolean(Const.Key.UPDATE_NOTIFICATION, true)) {
|
if (showNotification) {
|
||||||
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
|
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
|
||||||
ShowUI.managerUpdateNotification();
|
ShowUI.managerUpdateNotification();
|
||||||
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
|
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
|
@@ -7,6 +7,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
@@ -39,8 +40,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
|
|
||||||
private boolean unzipAndCheck() throws Exception {
|
private boolean unzipAndCheck() throws Exception {
|
||||||
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
|
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
|
||||||
String s = Utils.cmd("head -n 1 " + new File(mCachedFile.getParentFile(), "updater-script"));
|
return ShellUtils.fastCmdResult("grep -q '#MAGISK' " + new File(mCachedFile.getParentFile(), "updater-script"));
|
||||||
return s != null && s.contains("#MAGISK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,7 +93,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
switch (result) {
|
switch (result) {
|
||||||
case -1:
|
case -1:
|
||||||
console.add("! Installation failed");
|
console.add("! Installation failed");
|
||||||
Utils.showUriSnack(getActivity(), mUri);
|
SnackbarMaker.showUri(getActivity(), mUri);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
console.add("! This zip is not a Magisk Module!");
|
console.add("! This zip is not a Magisk Module!");
|
95
src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java
Normal file
95
src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.PatchAPK;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
public class HideManager extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
|
||||||
|
public HideManager(Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String genPackageName(String prefix, int length) {
|
||||||
|
StringBuilder builder = new StringBuilder(length);
|
||||||
|
builder.append(prefix);
|
||||||
|
length -= prefix.length();
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
String base = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
String alpha = base + base.toUpperCase();
|
||||||
|
String full = alpha + "0123456789..........";
|
||||||
|
char next, prev = '\0';
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
if (prev == '.' || i == length - 1 || i == 0) {
|
||||||
|
next = alpha.charAt(random.nextInt(alpha.length()));
|
||||||
|
} else {
|
||||||
|
next = full.charAt(random.nextInt(full.length()));
|
||||||
|
}
|
||||||
|
builder.append(next);
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
dialog = ProgressDialog.show(getActivity(),
|
||||||
|
getActivity().getString(R.string.hide_manager_toast),
|
||||||
|
getActivity().getString(R.string.hide_manager_toast2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
MagiskManager mm = MagiskManager.get();
|
||||||
|
|
||||||
|
// Generate a new app with random package name
|
||||||
|
SuFile repack = new SuFile("/data/local/tmp/repack.apk");
|
||||||
|
String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!PatchAPK.patchPackageID(
|
||||||
|
mm.getPackageCodePath(),
|
||||||
|
new SuFileOutputStream(repack),
|
||||||
|
Const.ORIG_PKG_NAME, pkg))
|
||||||
|
return false;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install the application
|
||||||
|
if (!ShellUtils.fastCmdResult(Shell.getShell(), "pm install " + repack))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
repack.delete();
|
||||||
|
|
||||||
|
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
|
||||||
|
mm.dumpPrefs();
|
||||||
|
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean b) {
|
||||||
|
dialog.dismiss();
|
||||||
|
if (!b) {
|
||||||
|
MagiskManager.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
super.onPostExecute(b);
|
||||||
|
}
|
||||||
|
}
|
327
src/full/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
Normal file
327
src/full/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.container.TarEntry;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
import com.topjohnwu.utils.SignBoot;
|
||||||
|
|
||||||
|
import org.kamranzafar.jtar.TarInputStream;
|
||||||
|
import org.kamranzafar.jtar.TarOutputStream;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
private static final int PATCH_MODE = 0;
|
||||||
|
public static final int DIRECT_MODE = 1;
|
||||||
|
private static final int FIX_ENV_MODE = 2;
|
||||||
|
public static final int SECOND_SLOT_MODE = 3;
|
||||||
|
|
||||||
|
private Uri bootUri, mZip;
|
||||||
|
private List<String> console, logs;
|
||||||
|
private String mBoot;
|
||||||
|
private int mode;
|
||||||
|
private File installDir;
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
private MagiskManager mm;
|
||||||
|
|
||||||
|
public InstallMagisk(Activity context, Uri zip) {
|
||||||
|
super(context);
|
||||||
|
mZip = zip;
|
||||||
|
mm = MagiskManager.get();
|
||||||
|
mode = FIX_ENV_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstallMagisk(Activity context, List<String> console, List<String> logs, Uri zip, int mode) {
|
||||||
|
this(context, zip);
|
||||||
|
this.console = console;
|
||||||
|
this.logs = logs;
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri zip, Uri boot) {
|
||||||
|
this(context, console, logs, zip, PATCH_MODE);
|
||||||
|
bootUri = boot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
Activity a = getActivity();
|
||||||
|
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
|
||||||
|
console = new NOPList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractFiles(String arch) throws IOException {
|
||||||
|
console.add("- Extracting files");
|
||||||
|
try (InputStream in = mm.getContentResolver().openInputStream(mZip)) {
|
||||||
|
if (in == null) throw new FileNotFoundException();
|
||||||
|
BufferedInputStream buf = new BufferedInputStream(in);
|
||||||
|
buf.mark(Integer.MAX_VALUE);
|
||||||
|
ZipUtils.unzip(buf, installDir, arch + "/", true);
|
||||||
|
buf.reset();
|
||||||
|
ZipUtils.unzip(buf, installDir, "common/", true);
|
||||||
|
buf.reset();
|
||||||
|
ZipUtils.unzip(buf, installDir, "chromeos/", false);
|
||||||
|
buf.reset();
|
||||||
|
ZipUtils.unzip(buf, installDir, "META-INF/com/google/android/update-binary", true);
|
||||||
|
buf.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
console.add("! Invalid Uri");
|
||||||
|
throw e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
console.add("! Cannot unzip zip");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
Shell.Sync.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
|
||||||
|
installDir, installDir, installDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean dumpBoot() {
|
||||||
|
console.add("- Copying image locally");
|
||||||
|
// Copy boot image to local
|
||||||
|
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
|
||||||
|
OutputStream out = new FileOutputStream(mBoot)
|
||||||
|
) {
|
||||||
|
if (in == null)
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
|
||||||
|
InputStream src;
|
||||||
|
if (Utils.getNameFromUri(mm, bootUri).endsWith(".tar")) {
|
||||||
|
// Extract boot.img from tar
|
||||||
|
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
|
||||||
|
org.kamranzafar.jtar.TarEntry entry;
|
||||||
|
while ((entry = tar.getNextEntry()) != null) {
|
||||||
|
if (entry.getName().equals("boot.img"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
src = tar;
|
||||||
|
} else {
|
||||||
|
// Direct copy raw image
|
||||||
|
src = new BufferedInputStream(in);
|
||||||
|
}
|
||||||
|
ShellUtils.pump(src, out);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
console.add("! Invalid Uri");
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
console.add("! Copy failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File patchBoot() throws IOException {
|
||||||
|
boolean isSigned;
|
||||||
|
try (InputStream in = new SuFileInputStream(mBoot)) {
|
||||||
|
isSigned = SignBoot.verifySignature(in, null);
|
||||||
|
if (isSigned) {
|
||||||
|
console.add("- Boot image is signed with AVB 1.0");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
console.add("! Unable to check signature");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch boot image
|
||||||
|
Shell.Sync.sh(console, logs,
|
||||||
|
"cd " + installDir,
|
||||||
|
Utils.fmt("KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep " +
|
||||||
|
"boot_patch.sh %s || echo 'Failed!'",
|
||||||
|
mm.keepEnc, mm.keepVerity, mBoot));
|
||||||
|
|
||||||
|
if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Shell.Sync.sh("mv bin/busybox busybox",
|
||||||
|
"rm -rf magisk.apk bin boot.img update-binary",
|
||||||
|
"cd /");
|
||||||
|
|
||||||
|
File patched = new File(installDir, "new-boot.img");
|
||||||
|
if (isSigned) {
|
||||||
|
console.add("- Signing boot image with test keys");
|
||||||
|
File signed = new File(installDir, "signed.img");
|
||||||
|
try (InputStream in = new SuFileInputStream(patched);
|
||||||
|
OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))
|
||||||
|
) {
|
||||||
|
SignBoot.doSignature("/boot", in, out, null, null);
|
||||||
|
}
|
||||||
|
Shell.Sync.su("mv -f " + signed + " " + patched);
|
||||||
|
}
|
||||||
|
return patched;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void outputBoot(File patched) throws IOException {
|
||||||
|
switch (mode) {
|
||||||
|
case PATCH_MODE:
|
||||||
|
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + mm.bootFormat);
|
||||||
|
dest.getParentFile().mkdirs();
|
||||||
|
OutputStream out;
|
||||||
|
switch (mm.bootFormat) {
|
||||||
|
case ".img.tar":
|
||||||
|
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
|
||||||
|
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case ".img":
|
||||||
|
out = new BufferedOutputStream(new FileOutputStream(dest));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try (InputStream in = new SuFileInputStream(patched)) {
|
||||||
|
ShellUtils.pump(in, out);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
Shell.Sync.su("rm -f " + patched);
|
||||||
|
console.add("");
|
||||||
|
console.add("****************************");
|
||||||
|
console.add(" Patched image is placed in ");
|
||||||
|
console.add(" " + dest + " ");
|
||||||
|
console.add("****************************");
|
||||||
|
break;
|
||||||
|
case SECOND_SLOT_MODE:
|
||||||
|
case DIRECT_MODE:
|
||||||
|
Shell.Sync.sh(console, logs,
|
||||||
|
Utils.fmt("direct_install %s %s %s", patched, mBoot, installDir));
|
||||||
|
if (!mm.keepVerity)
|
||||||
|
Shell.Sync.sh(console, logs, "find_dtbo_image", "patch_dtbo_image");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
installDir = new File("/data/adb/magisk");
|
||||||
|
Shell.Sync.sh("rm -rf /data/adb/magisk/*");
|
||||||
|
} else {
|
||||||
|
installDir = new File(
|
||||||
|
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
|
||||||
|
mm.createDeviceProtectedStorageContext() : mm)
|
||||||
|
.getFilesDir().getParent()
|
||||||
|
, "install");
|
||||||
|
Shell.Sync.sh("rm -rf " + installDir);
|
||||||
|
installDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case PATCH_MODE:
|
||||||
|
mBoot = new File(installDir, "boot.img").getAbsolutePath();
|
||||||
|
if (!dumpBoot())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case DIRECT_MODE:
|
||||||
|
console.add("- Detecting target image");
|
||||||
|
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
|
||||||
|
break;
|
||||||
|
case SECOND_SLOT_MODE:
|
||||||
|
console.add("- Detecting target image");
|
||||||
|
char slot[] = ShellUtils.fastCmd("echo $SLOT").toCharArray();
|
||||||
|
if (slot[1] == 'a') slot[1] = 'b';
|
||||||
|
else slot[1] = 'a';
|
||||||
|
mBoot = ShellUtils.fastCmd("SLOT=" + String.valueOf(slot),
|
||||||
|
"find_boot_image", "echo \"$BOOTIMAGE\"");
|
||||||
|
Shell.Async.su("mount_partitions");
|
||||||
|
break;
|
||||||
|
case FIX_ENV_MODE:
|
||||||
|
mBoot = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mBoot == null) {
|
||||||
|
console.add("! Unable to detect target image");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.add("- Target image: " + mBoot);
|
||||||
|
|
||||||
|
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
|
||||||
|
String arch;
|
||||||
|
|
||||||
|
if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.SEPOL_REFACTOR) {
|
||||||
|
// 32-bit only
|
||||||
|
if (abis.contains("x86")) arch = "x86";
|
||||||
|
else arch = "arm";
|
||||||
|
} else {
|
||||||
|
if (abis.contains("x86_64")) arch = "x64";
|
||||||
|
else if (abis.contains("arm64-v8a")) arch = "arm64";
|
||||||
|
else if (abis.contains("x86")) arch = "x86";
|
||||||
|
else arch = "arm";
|
||||||
|
}
|
||||||
|
|
||||||
|
console.add("- Device platform: " + Build.SUPPORTED_ABIS[0]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
extractFiles(arch);
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
Shell.Sync.sh("fix_env");
|
||||||
|
} else {
|
||||||
|
File patched = patchBoot();
|
||||||
|
if (patched == null)
|
||||||
|
return false;
|
||||||
|
outputBoot(patched);
|
||||||
|
console.add("- All done!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
dialog.dismiss();
|
||||||
|
MagiskManager.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
|
||||||
|
} else {
|
||||||
|
// Running in FlashActivity
|
||||||
|
FlashActivity activity = (FlashActivity) getActivity();
|
||||||
|
if (!result) {
|
||||||
|
Shell.Async.sh("rm -rf " + installDir);
|
||||||
|
console.add("! Installation failed");
|
||||||
|
activity.reboot.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
activity.buttonPanel.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NOPList<E> extends AbstractList<E> {
|
||||||
|
@Override
|
||||||
|
public E get(int index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, E element) {}
|
||||||
|
}
|
||||||
|
}
|
@@ -11,7 +11,7 @@ import java.util.List;
|
|||||||
public class LoadModules extends ParallelTask<Void, Void, Void> {
|
public class LoadModules extends ParallelTask<Void, Void, Void> {
|
||||||
|
|
||||||
private List<String> getModList() {
|
private List<String> getModList() {
|
||||||
String command = "ls -d " + Const.MAGISK_PATH() + "/* | grep -v lost+found";
|
String command = "ls -d " + Const.MAGISK_PATH + "/* | grep -v lost+found";
|
||||||
return Shell.Sync.su(command);
|
return Shell.Sync.su(command);
|
||||||
}
|
}
|
||||||
|
|
@@ -54,11 +54,13 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
|||||||
}
|
}
|
||||||
String css;
|
String css;
|
||||||
try (
|
try (
|
||||||
InputStream in = mm.getAssets().open(mm.isDarkTheme ? "dark.css" : "light.css");
|
InputStream in = mm.getResources().openRawResource(
|
||||||
|
mm.isDarkTheme ? R.raw.dark : R.raw.light);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
||||||
) {
|
) {
|
||||||
ShellUtils.pump(in, out);
|
ShellUtils.pump(in, out);
|
||||||
css = out.toString();
|
css = out.toString();
|
||||||
|
in.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return "";
|
return "";
|
@@ -12,8 +12,8 @@ import android.widget.Toast;
|
|||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
@@ -90,7 +90,6 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
do {
|
do {
|
||||||
conn = WebService.request(mLink, null);
|
conn = WebService.request(mLink, null);
|
||||||
if (conn == null) return null;
|
|
||||||
total = conn.getContentLength();
|
total = conn.getContentLength();
|
||||||
if (total < 0)
|
if (total < 0)
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
@@ -147,7 +146,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
Utils.showUriSnack(activity, uri);
|
SnackbarMaker.showUri(activity, uri);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MagiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
|
MagiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
|
||||||
@@ -157,7 +156,8 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParallelTask<Void, Object, Boolean> exec(Void... voids) {
|
public ParallelTask<Void, Object, Boolean> exec(Void... voids) {
|
||||||
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
com.topjohnwu.magisk.components.Activity.runWithPermission(
|
||||||
|
getActivity(), new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
|
||||||
() -> super.exec(voids));
|
() -> super.exec(voids));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
@@ -1,31 +1,35 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
public class RestoreImages extends ParallelTask<Void, Void, Boolean> {
|
public class RestoreImages extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
|
||||||
|
public RestoreImages(Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Activity a = getActivity();
|
||||||
|
dialog = ProgressDialog.show(a, a.getString(R.string.restore_img), a.getString(R.string.restore_img_msg));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... voids) {
|
protected Boolean doInBackground(Void... voids) {
|
||||||
String sha1;
|
return ShellUtils.fastCmdResult("restore_imgs");
|
||||||
sha1 = Utils.cmd("cat /.backup/.sha1");
|
|
||||||
if (sha1 == null) {
|
|
||||||
sha1 = Utils.cmd("cat /init.magisk.rc | grep STOCKSHA1");
|
|
||||||
if (sha1 == null)
|
|
||||||
return false;
|
|
||||||
sha1 = sha1.substring(sha1.indexOf('=') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShellUtils.fastCmdResult(Shell.getShell(), "restore_imgs " + sha1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
|
dialog.cancel();
|
||||||
if (result) {
|
if (result) {
|
||||||
MagiskManager.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
MagiskManager.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
||||||
} else {
|
} else {
|
210
src/full/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java
Normal file
210
src/full/java/com/topjohnwu/magisk/asyncs/UpdateRepos.java
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.ReposFragment;
|
||||||
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
||||||
|
|
||||||
|
private static final int CHECK_ETAG = 0;
|
||||||
|
private static final int LOAD_NEXT = 1;
|
||||||
|
private static final int LOAD_PREV = 2;
|
||||||
|
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||||
|
|
||||||
|
private MagiskManager mm;
|
||||||
|
private List<String> etags, newEtags = new LinkedList<>();
|
||||||
|
private Set<String> cached;
|
||||||
|
private boolean forceUpdate;
|
||||||
|
private AtomicInteger taskCount = new AtomicInteger(0);
|
||||||
|
final private Object allDone = new Object();
|
||||||
|
|
||||||
|
public UpdateRepos(boolean force) {
|
||||||
|
mm = MagiskManager.get();
|
||||||
|
mm.repoLoadDone.reset();
|
||||||
|
forceUpdate = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueTask(Runnable task) {
|
||||||
|
// Thread pool's queue has an upper bound, batch it with 64 tasks
|
||||||
|
while (taskCount.get() >= 64) {
|
||||||
|
waitTasks();
|
||||||
|
}
|
||||||
|
taskCount.incrementAndGet();
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
task.run();
|
||||||
|
if (taskCount.decrementAndGet() == 0) {
|
||||||
|
synchronized (allDone) {
|
||||||
|
allDone.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitTasks() {
|
||||||
|
if (taskCount.get() == 0)
|
||||||
|
return;
|
||||||
|
synchronized (allDone) {
|
||||||
|
try {
|
||||||
|
allDone.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Wait again
|
||||||
|
waitTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
|
||||||
|
JSONArray jsonArray = new JSONArray(jsonString);
|
||||||
|
|
||||||
|
// Empty page, halt
|
||||||
|
if (jsonArray.length() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
||||||
|
String id = rawRepo.getString("description");
|
||||||
|
String name = rawRepo.getString("name");
|
||||||
|
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
|
||||||
|
Set<String> set = Collections.synchronizedSet(cached);
|
||||||
|
queueTask(() -> {
|
||||||
|
Repo repo = mm.repoDB.getRepo(id);
|
||||||
|
try {
|
||||||
|
if (repo == null)
|
||||||
|
repo = new Repo(name);
|
||||||
|
else
|
||||||
|
set.remove(id);
|
||||||
|
repo.update(date);
|
||||||
|
mm.repoDB.addRepo(repo);
|
||||||
|
publishProgress();
|
||||||
|
} catch (Repo.IllegalRepoException e) {
|
||||||
|
Logger.debug(e.getMessage());
|
||||||
|
mm.repoDB.removeRepo(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean loadPage(int page, int mode) {
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
if (mode == CHECK_ETAG && page < etags.size())
|
||||||
|
header.put(Const.Key.IF_NONE_MATCH, etags.get(page));
|
||||||
|
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpURLConnection conn = WebService.request(url, header);
|
||||||
|
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||||
|
// Current page is not updated, check the next page
|
||||||
|
return loadPage(page + 1, CHECK_ETAG);
|
||||||
|
}
|
||||||
|
if (!loadJSON(WebService.getString(conn)))
|
||||||
|
return mode != CHECK_ETAG;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If one page is updated, we force update all pages */
|
||||||
|
|
||||||
|
// Update ETAG
|
||||||
|
String etag = header.get(Const.Key.ETAG_KEY);
|
||||||
|
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||||
|
if (mode == LOAD_PREV) {
|
||||||
|
// We are loading a previous page, push the new tag to the front
|
||||||
|
newEtags.add(0, etag);
|
||||||
|
} else {
|
||||||
|
newEtags.add(etag);
|
||||||
|
}
|
||||||
|
|
||||||
|
String links = header.get(Const.Key.LINK_KEY);
|
||||||
|
if (links != null) {
|
||||||
|
for (String s : links.split(", ")) {
|
||||||
|
if (mode != LOAD_PREV && s.contains("next")) {
|
||||||
|
// Force load all next pages
|
||||||
|
loadPage(page + 1, LOAD_NEXT);
|
||||||
|
}
|
||||||
|
if (mode != LOAD_NEXT && s.contains("prev")) {
|
||||||
|
// Back propagation
|
||||||
|
loadPage(page - 1, LOAD_PREV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Void... values) {
|
||||||
|
if (ReposFragment.adapter != null)
|
||||||
|
ReposFragment.adapter.notifyDBChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
mm.repoLoadDone.setPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(","));
|
||||||
|
cached = mm.repoDB.getRepoIDSet();
|
||||||
|
|
||||||
|
if (loadPage(0, CHECK_ETAG)) {
|
||||||
|
waitTasks();
|
||||||
|
|
||||||
|
// The leftover cached means they are removed from online repo
|
||||||
|
mm.repoDB.removeRepo(cached);
|
||||||
|
|
||||||
|
// Update ETag
|
||||||
|
mm.prefs.edit().putString(Const.Key.ETAG_KEY, TextUtils.join(",", newEtags)).apply();
|
||||||
|
} else if (forceUpdate) {
|
||||||
|
Cursor c = mm.repoDB.getRawCursor();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Repo repo = new Repo(c);
|
||||||
|
queueTask(() -> {
|
||||||
|
try {
|
||||||
|
repo.update();
|
||||||
|
mm.repoDB.addRepo(repo);
|
||||||
|
} catch (Repo.IllegalRepoException e) {
|
||||||
|
Logger.debug(e.getMessage());
|
||||||
|
mm.repoDB.removeRepo(repo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
waitTasks();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void v) {
|
||||||
|
mm.repoLoadDone.publish();
|
||||||
|
super.onPostExecute(v);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,108 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Keep;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.StyleRes;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
|
public abstract class FlavorActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private AssetManager swappedAssetManager = null;
|
||||||
|
private Resources swappedResources = null;
|
||||||
|
private Resources.Theme backupTheme = null;
|
||||||
|
|
||||||
|
@StyleRes
|
||||||
|
public int getDarkTheme() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MagiskManager getMagiskManager() {
|
||||||
|
return (MagiskManager) super.getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (this instanceof Topic.Subscriber) {
|
||||||
|
((Topic.Subscriber) this).subscribeTopics();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getMagiskManager().isDarkTheme && getDarkTheme() != -1) {
|
||||||
|
setTheme(getDarkTheme());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
if (this instanceof Topic.Subscriber) {
|
||||||
|
((Topic.Subscriber) this).unsubscribeTopics();
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setFloating() {
|
||||||
|
boolean isTablet = getResources().getBoolean(R.bool.isTablet);
|
||||||
|
if (isTablet) {
|
||||||
|
WindowManager.LayoutParams params = getWindow().getAttributes();
|
||||||
|
params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
|
||||||
|
params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
|
||||||
|
params.alpha = 1.0f;
|
||||||
|
params.dimAmount = 0.6f;
|
||||||
|
params.flags |= 2;
|
||||||
|
getWindow().setAttributes(params);
|
||||||
|
setFinishOnTouchOutside(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resources.Theme getTheme() {
|
||||||
|
return backupTheme == null ? super.getTheme() : backupTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssetManager getAssets() {
|
||||||
|
return swappedAssetManager == null ? super.getAssets() : swappedAssetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AssetManager getAssets(String apk) {
|
||||||
|
try {
|
||||||
|
AssetManager asset = AssetManager.class.newInstance();
|
||||||
|
AssetManager.class.getMethod("addAssetPath", String.class).invoke(asset, apk);
|
||||||
|
return asset;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resources getResources() {
|
||||||
|
return swappedResources == null ? super.getResources() : swappedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public void swapResources(String dexPath) {
|
||||||
|
AssetManager asset = getAssets(dexPath);
|
||||||
|
if (asset != null) {
|
||||||
|
backupTheme = super.getTheme();
|
||||||
|
Resources res = super.getResources();
|
||||||
|
swappedResources = new Resources(asset, res.getDisplayMetrics(), res.getConfiguration());
|
||||||
|
swappedAssetManager = asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public void restoreResources() {
|
||||||
|
swappedAssetManager = null;
|
||||||
|
swappedResources = null;
|
||||||
|
backupTheme = null;
|
||||||
|
}
|
||||||
|
}
|
@@ -36,4 +36,8 @@ public class Fragment extends android.support.v4.app.Fragment {
|
|||||||
public void startActivityForResult(Intent intent, int requestCode, Activity.ActivityResultListener listener) {
|
public void startActivityForResult(Intent intent, int requestCode, Activity.ActivityResultListener listener) {
|
||||||
((Activity) getActivity()).startActivityForResult(intent, requestCode, listener);
|
((Activity) getActivity()).startActivityForResult(intent, requestCode, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void runWithPermission(String[] permissions, Runnable callback) {
|
||||||
|
Activity.runWithPermission(getActivity(), permissions, callback);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,11 +1,15 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.net.Uri;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
public class SnackbarMaker {
|
public class SnackbarMaker {
|
||||||
|
|
||||||
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
||||||
@@ -34,4 +38,10 @@ public class SnackbarMaker {
|
|||||||
text.setMaxLines(Integer.MAX_VALUE);
|
text.setMaxLines(Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showUri(Activity activity, Uri uri) {
|
||||||
|
make(activity, activity.getString(R.string.internal_storage,
|
||||||
|
"/MagiskManager/" + Utils.getNameFromUri(activity, uri)),
|
||||||
|
Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.ok, (v)->{}).show();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -3,8 +3,6 @@ package com.topjohnwu.magisk.container;
|
|||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class Module extends BaseModule {
|
public class Module extends BaseModule {
|
||||||
|
|
||||||
private SuFile mRemoveFile, mDisableFile, mUpdateFile;
|
private SuFile mRemoveFile, mDisableFile, mUpdateFile;
|
||||||
@@ -13,12 +11,12 @@ public class Module extends BaseModule {
|
|||||||
public Module(String path) {
|
public Module(String path) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parseProps(Shell.Sync.su("cat " + path + "/module.prop"));
|
parseProps(Shell.Sync.su("dos2unix < " + path + "/module.prop"));
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {}
|
||||||
|
|
||||||
mRemoveFile = new SuFile(path + "/remove", true);
|
mRemoveFile = new SuFile(path + "/remove");
|
||||||
mDisableFile = new SuFile(path + "/disable", true);
|
mDisableFile = new SuFile(path + "/disable");
|
||||||
mUpdateFile = new SuFile(path + "/update", true);
|
mUpdateFile = new SuFile(path + "/update");
|
||||||
|
|
||||||
if (getId() == null) {
|
if (getId() == null) {
|
||||||
int sep = path.lastIndexOf('/');
|
int sep = path.lastIndexOf('/');
|
||||||
@@ -36,9 +34,7 @@ public class Module extends BaseModule {
|
|||||||
|
|
||||||
public void createDisableFile() {
|
public void createDisableFile() {
|
||||||
mEnable = false;
|
mEnable = false;
|
||||||
try {
|
mDisableFile.createNewFile();
|
||||||
mDisableFile.createNewFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDisableFile() {
|
public void removeDisableFile() {
|
||||||
@@ -52,9 +48,7 @@ public class Module extends BaseModule {
|
|||||||
|
|
||||||
public void createRemoveFile() {
|
public void createRemoveFile() {
|
||||||
mRemove = true;
|
mRemove = true;
|
||||||
try {
|
mRemoveFile.createNewFile();
|
||||||
mRemoveFile.createNewFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteRemoveFile() {
|
public void deleteRemoveFile() {
|
@@ -20,7 +20,8 @@ public class Policy implements Comparable<Policy>{
|
|||||||
|
|
||||||
public Policy(int uid, PackageManager pm) throws PackageManager.NameNotFoundException {
|
public Policy(int uid, PackageManager pm) throws PackageManager.NameNotFoundException {
|
||||||
String[] pkgs = pm.getPackagesForUid(uid);
|
String[] pkgs = pm.getPackagesForUid(uid);
|
||||||
if (pkgs == null || pkgs.length == 0) throw new PackageManager.NameNotFoundException();
|
if (pkgs == null || pkgs.length == 0)
|
||||||
|
throw new PackageManager.NameNotFoundException();
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
packageName = pkgs[0];
|
packageName = pkgs[0];
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
@@ -35,6 +36,8 @@ public class Policy implements Comparable<Policy>{
|
|||||||
logging = c.getInt(c.getColumnIndex("logging")) != 0;
|
logging = c.getInt(c.getColumnIndex("logging")) != 0;
|
||||||
notification = c.getInt(c.getColumnIndex("notification")) != 0;
|
notification = c.getInt(c.getColumnIndex("notification")) != 0;
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
|
if (info.uid != uid)
|
||||||
|
throw new PackageManager.NameNotFoundException();
|
||||||
appName = info.loadLabel(pm).toString();
|
appName = info.loadLabel(pm).toString();
|
||||||
}
|
}
|
||||||
|
|
@@ -5,6 +5,8 @@ import android.database.Cursor;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
@@ -15,10 +17,8 @@ public class Repo extends BaseModule {
|
|||||||
private String repoName;
|
private String repoName;
|
||||||
private Date mLastUpdate;
|
private Date mLastUpdate;
|
||||||
|
|
||||||
public Repo(String name, Date lastUpdate) throws IllegalRepoException {
|
public Repo(String name) {
|
||||||
mLastUpdate = lastUpdate;
|
|
||||||
repoName = name;
|
repoName = name;
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Repo(Cursor c) {
|
public Repo(Cursor c) {
|
||||||
@@ -28,10 +28,9 @@ public class Repo extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update() throws IllegalRepoException {
|
public void update() throws IllegalRepoException {
|
||||||
String props = WebService.getString(getManifestUrl());
|
String props[] = Utils.dos2unix(WebService.getString(getManifestUrl())).split("\\n");
|
||||||
String lines[] = props.split("\\n");
|
|
||||||
try {
|
try {
|
||||||
parseProps(lines);
|
parseProps(props);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
|
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -42,18 +41,14 @@ public class Repo extends BaseModule {
|
|||||||
if (getVersionCode() < 0) {
|
if (getVersionCode() < 0) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
||||||
}
|
}
|
||||||
if (getMinMagiskVersion() < Const.MIN_MODULE_VER) {
|
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] is outdated");
|
Logger.debug("Repo [" + repoName + "] is outdated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean update(Date lastUpdate) throws IllegalRepoException {
|
public void update(Date lastUpdate) throws IllegalRepoException {
|
||||||
if (lastUpdate.after(mLastUpdate)) {
|
mLastUpdate = lastUpdate;
|
||||||
mLastUpdate = lastUpdate;
|
update();
|
||||||
update();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -7,6 +7,7 @@ import android.database.Cursor;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ import com.topjohnwu.magisk.container.SuLogEntry;
|
|||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
@@ -25,7 +27,7 @@ import java.util.Collections;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SuDatabaseHelper {
|
public class MagiskDatabaseHelper {
|
||||||
|
|
||||||
private static final int DATABASE_VER = 5;
|
private static final int DATABASE_VER = 5;
|
||||||
private static final String POLICY_TABLE = "policies";
|
private static final String POLICY_TABLE = "policies";
|
||||||
@@ -34,131 +36,107 @@ public class SuDatabaseHelper {
|
|||||||
private static final String STRINGS_TABLE = "strings";
|
private static final String STRINGS_TABLE = "strings";
|
||||||
|
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private SQLiteDatabase mDb;
|
private SQLiteDatabase db;
|
||||||
private File DB_FILE;
|
|
||||||
|
|
||||||
public static SuDatabaseHelper getInstance(MagiskManager mm) {
|
@NonNull
|
||||||
|
public static MagiskDatabaseHelper getInstance(MagiskManager mm) {
|
||||||
try {
|
try {
|
||||||
return new SuDatabaseHelper(mm);
|
return new MagiskDatabaseHelper(mm);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Let's cleanup everything and try again
|
// Let's cleanup everything and try again
|
||||||
cleanup("*");
|
Shell.Sync.su("db_clean '*'");
|
||||||
return new SuDatabaseHelper(mm);
|
return new MagiskDatabaseHelper(mm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanup() {
|
private MagiskDatabaseHelper(MagiskManager mm) {
|
||||||
cleanup(String.valueOf(Const.USER_ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void cleanup(String s) {
|
|
||||||
Shell.Sync.su(
|
|
||||||
"umount -l /data/user*/*/*/databases/su.db",
|
|
||||||
"umount -l /sbin/.core/db-" + s + "/magisk.db",
|
|
||||||
"rm -rf /sbin/.core/db-" + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SuDatabaseHelper(MagiskManager mm) {
|
|
||||||
pm = mm.getPackageManager();
|
pm = mm.getPackageManager();
|
||||||
mDb = openDatabase(mm);
|
db = openDatabase(mm);
|
||||||
int version = mDb.getVersion();
|
db.disableWriteAheadLogging();
|
||||||
|
int version = db.getVersion();
|
||||||
if (version < DATABASE_VER) {
|
if (version < DATABASE_VER) {
|
||||||
onUpgrade(mDb, version);
|
onUpgrade(db, version);
|
||||||
} else if (version > DATABASE_VER) {
|
} else if (version > DATABASE_VER) {
|
||||||
onDowngrade(mDb);
|
onDowngrade(db);
|
||||||
}
|
}
|
||||||
mDb.setVersion(DATABASE_VER);
|
db.setVersion(DATABASE_VER);
|
||||||
clearOutdated();
|
clearOutdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SQLiteDatabase openDatabase(MagiskManager mm) {
|
private SQLiteDatabase openDatabase(MagiskManager mm) {
|
||||||
String GLOBAL_DB = "/data/adb/magisk.db";
|
final File DB_FILE = new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
|
||||||
DB_FILE = new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
|
|
||||||
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
|
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
|
||||||
? mm.createDeviceProtectedStorageContext() : mm;
|
? mm.createDeviceProtectedStorageContext() : mm;
|
||||||
if (!DB_FILE.exists()) {
|
if (!DB_FILE.canWrite()) {
|
||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess()) {
|
||||||
// We don't want the app to crash, create a db and return
|
// We don't want the app to crash, create a db and return
|
||||||
DB_FILE = mm.getDatabasePath("su.db");
|
|
||||||
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
||||||
}
|
}
|
||||||
mm.loadMagiskInfo();
|
mm.loadMagiskInfo();
|
||||||
// Cleanup
|
// Cleanup
|
||||||
cleanup();
|
Shell.Sync.su("db_clean " + Const.USER_ID);
|
||||||
if (mm.magiskVersionCode < 1410) {
|
if (mm.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
|
||||||
// Super old legacy mode
|
// Super old legacy mode
|
||||||
DB_FILE = mm.getDatabasePath("su.db");
|
|
||||||
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
||||||
} else if (mm.magiskVersionCode < 1450) {
|
} else if (mm.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
|
||||||
// Legacy mode with FBE aware
|
// Legacy mode with FBE aware
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
de.moveDatabaseFrom(mm, "su.db");
|
de.moveDatabaseFrom(mm, "su.db");
|
||||||
}
|
}
|
||||||
DB_FILE = de.getDatabasePath("su.db");
|
|
||||||
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
||||||
} else {
|
} else {
|
||||||
|
// Global database
|
||||||
|
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
|
||||||
mm.deleteDatabase("su.db");
|
mm.deleteDatabase("su.db");
|
||||||
de.deleteDatabase("su.db");
|
de.deleteDatabase("su.db");
|
||||||
if (mm.magiskVersionCode < 1460) {
|
if (mm.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
|
||||||
// v14.5 global DB location
|
|
||||||
GLOBAL_DB = new File(de.getFilesDir().getParentFile().getParentFile(),
|
|
||||||
"magisk.db").getPath();
|
|
||||||
// We need some additional policies on old versions
|
// We need some additional policies on old versions
|
||||||
Shell.Sync.su("magiskpolicy --live 'create su_file' 'allow * su_file file *'");
|
Shell.Sync.su("db_sepatch");
|
||||||
|
}
|
||||||
|
if (!GLOBAL_DB.exists()) {
|
||||||
|
Shell.Sync.su("db_init");
|
||||||
|
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
|
||||||
|
Shell.Sync.su("db_restore");
|
||||||
}
|
}
|
||||||
// Touch global DB and setup db in tmpfs
|
|
||||||
Shell.Sync.su(Utils.fmt("touch %s; mkdir -p %s; touch %s; touch %s-journal;" +
|
|
||||||
"mount -o bind %s %s;" +
|
|
||||||
"chcon u:object_r:su_file:s0 %s/*; chown %d.%d %s;" +
|
|
||||||
"chmod 666 %s/*; chmod 700 %s;",
|
|
||||||
GLOBAL_DB, DB_FILE.getParent(), DB_FILE, DB_FILE,
|
|
||||||
GLOBAL_DB, DB_FILE,
|
|
||||||
DB_FILE.getParent(), Process.myUid(), Process.myUid(), DB_FILE.getParent(),
|
|
||||||
DB_FILE.getParent(), DB_FILE.getParent()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
Shell.Sync.su("db_setup " + Process.myUid());
|
||||||
}
|
}
|
||||||
// Not using legacy mode, open the mounted global DB
|
// Not using legacy mode, open the mounted global DB
|
||||||
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
|
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion) {
|
||||||
try {
|
if (oldVersion == 0) {
|
||||||
if (oldVersion == 0) {
|
createTables(db);
|
||||||
createTables(db);
|
oldVersion = 3;
|
||||||
oldVersion = 3;
|
}
|
||||||
}
|
if (oldVersion == 1) {
|
||||||
if (oldVersion == 1) {
|
// We're dropping column app_name, rename and re-construct table
|
||||||
// We're dropping column app_name, rename and re-construct table
|
db.execSQL(Utils.fmt("ALTER TABLE %s RENAME TO %s_old", POLICY_TABLE));
|
||||||
db.execSQL(Utils.fmt("ALTER TABLE %s RENAME TO %s_old", POLICY_TABLE));
|
|
||||||
|
|
||||||
// Create the new tables
|
// Create the new tables
|
||||||
createTables(db);
|
createTables(db);
|
||||||
|
|
||||||
// Migrate old data to new tables
|
// Migrate old data to new tables
|
||||||
db.execSQL(Utils.fmt("INSERT INTO %s SELECT " +
|
db.execSQL(Utils.fmt("INSERT INTO %s SELECT " +
|
||||||
"uid, package_name, policy, until, logging, notification FROM %s_old",
|
"uid, package_name, policy, until, logging, notification FROM %s_old",
|
||||||
POLICY_TABLE, POLICY_TABLE));
|
POLICY_TABLE, POLICY_TABLE));
|
||||||
db.execSQL(Utils.fmt("DROP TABLE %s_old", POLICY_TABLE));
|
db.execSQL(Utils.fmt("DROP TABLE %s_old", POLICY_TABLE));
|
||||||
|
|
||||||
MagiskManager.get().deleteDatabase("sulog.db");
|
MagiskManager.get().deleteDatabase("sulog.db");
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
}
|
||||||
if (oldVersion == 2) {
|
if (oldVersion == 2) {
|
||||||
db.execSQL(Utils.fmt("UPDATE %s SET time=time*1000", LOG_TABLE));
|
db.execSQL(Utils.fmt("UPDATE %s SET time=time*1000", LOG_TABLE));
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
}
|
||||||
if (oldVersion == 3) {
|
if (oldVersion == 3) {
|
||||||
db.execSQL(Utils.fmt("CREATE TABLE IF NOT EXISTS %s (key TEXT, value TEXT, PRIMARY KEY(key))", STRINGS_TABLE));
|
db.execSQL(Utils.fmt("CREATE TABLE IF NOT EXISTS %s (key TEXT, value TEXT, PRIMARY KEY(key))", STRINGS_TABLE));
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
}
|
||||||
if (oldVersion == 4) {
|
if (oldVersion == 4) {
|
||||||
db.execSQL(Utils.fmt("UPDATE %s SET uid=uid%%100000", POLICY_TABLE));
|
db.execSQL(Utils.fmt("UPDATE %s SET uid=uid%%100000", POLICY_TABLE));
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
onDowngrade(db);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,9 +172,9 @@ public class SuDatabaseHelper {
|
|||||||
|
|
||||||
public void clearOutdated() {
|
public void clearOutdated() {
|
||||||
// Clear outdated policies
|
// Clear outdated policies
|
||||||
mDb.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);
|
db.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);
|
||||||
// Clear outdated logs
|
// Clear outdated logs
|
||||||
mDb.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - MagiskManager.get().suLogTimeout * 86400000), null);
|
db.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - MagiskManager.get().suLogTimeout * 86400000), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePolicy(Policy policy) {
|
public void deletePolicy(Policy policy) {
|
||||||
@@ -204,16 +182,16 @@ public class SuDatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deletePolicy(String pkg) {
|
public void deletePolicy(String pkg) {
|
||||||
mDb.delete(POLICY_TABLE, "package_name=?", new String[] { pkg });
|
db.delete(POLICY_TABLE, "package_name=?", new String[] { pkg });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePolicy(int uid) {
|
public void deletePolicy(int uid) {
|
||||||
mDb.delete(POLICY_TABLE, Utils.fmt("uid=%d", uid), null);
|
db.delete(POLICY_TABLE, Utils.fmt("uid=%d", uid), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Policy getPolicy(int uid) {
|
public Policy getPolicy(int uid) {
|
||||||
Policy policy = null;
|
Policy policy = null;
|
||||||
try (Cursor c = mDb.query(POLICY_TABLE, null, Utils.fmt("uid=%d", uid), null, null, null, null)) {
|
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid=%d", uid), null, null, null, null)) {
|
||||||
if (c.moveToNext()) {
|
if (c.moveToNext()) {
|
||||||
policy = new Policy(c, pm);
|
policy = new Policy(c, pm);
|
||||||
}
|
}
|
||||||
@@ -225,15 +203,15 @@ public class SuDatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addPolicy(Policy policy) {
|
public void addPolicy(Policy policy) {
|
||||||
mDb.replace(POLICY_TABLE, null, policy.getContentValues());
|
db.replace(POLICY_TABLE, null, policy.getContentValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePolicy(Policy policy) {
|
public void updatePolicy(Policy policy) {
|
||||||
mDb.update(POLICY_TABLE, policy.getContentValues(), Utils.fmt("uid=%d", policy.uid), null);
|
db.update(POLICY_TABLE, policy.getContentValues(), Utils.fmt("uid=%d", policy.uid), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Policy> getPolicyList(PackageManager pm) {
|
public List<Policy> getPolicyList(PackageManager pm) {
|
||||||
try (Cursor c = mDb.query(POLICY_TABLE, null, Utils.fmt("uid/100000=%d", Const.USER_ID),
|
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid/100000=%d", Const.USER_ID),
|
||||||
null, null, null, null)) {
|
null, null, null, null)) {
|
||||||
List<Policy> ret = new ArrayList<>(c.getCount());
|
List<Policy> ret = new ArrayList<>(c.getCount());
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
@@ -251,7 +229,7 @@ public class SuDatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<List<Integer>> getLogStructure() {
|
public List<List<Integer>> getLogStructure() {
|
||||||
try (Cursor c = mDb.query(LOG_TABLE, new String[] { "time" }, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
try (Cursor c = db.query(LOG_TABLE, new String[] { "time" }, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
||||||
null, null, null, "time DESC")) {
|
null, null, null, "time DESC")) {
|
||||||
List<List<Integer>> ret = new ArrayList<>();
|
List<List<Integer>> ret = new ArrayList<>();
|
||||||
List<Integer> list = null;
|
List<Integer> list = null;
|
||||||
@@ -271,28 +249,28 @@ public class SuDatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getLogCursor() {
|
public Cursor getLogCursor() {
|
||||||
return mDb.query(LOG_TABLE, null, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
return db.query(LOG_TABLE, null, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
||||||
null, null, null, "time DESC");
|
null, null, null, "time DESC");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLog(SuLogEntry log) {
|
public void addLog(SuLogEntry log) {
|
||||||
mDb.insert(LOG_TABLE, null, log.getContentValues());
|
db.insert(LOG_TABLE, null, log.getContentValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearLogs() {
|
public void clearLogs() {
|
||||||
mDb.delete(LOG_TABLE, null, null);
|
db.delete(LOG_TABLE, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSettings(String key, int value) {
|
public void setSettings(String key, int value) {
|
||||||
ContentValues data = new ContentValues();
|
ContentValues data = new ContentValues();
|
||||||
data.put("key", key);
|
data.put("key", key);
|
||||||
data.put("value", value);
|
data.put("value", value);
|
||||||
mDb.replace(SETTINGS_TABLE, null, data);
|
db.replace(SETTINGS_TABLE, null, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSettings(String key, int defaultValue) {
|
public int getSettings(String key, int defaultValue) {
|
||||||
int value = defaultValue;
|
int value = defaultValue;
|
||||||
try (Cursor c = mDb.query(SETTINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
try (Cursor c = db.query(SETTINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
||||||
if (c.moveToNext()) {
|
if (c.moveToNext()) {
|
||||||
value = c.getInt(c.getColumnIndex("value"));
|
value = c.getInt(c.getColumnIndex("value"));
|
||||||
}
|
}
|
||||||
@@ -302,27 +280,22 @@ public class SuDatabaseHelper {
|
|||||||
|
|
||||||
public void setStrings(String key, String value) {
|
public void setStrings(String key, String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
mDb.delete(STRINGS_TABLE, "key=?", new String[] { key });
|
db.delete(STRINGS_TABLE, "key=?", new String[] { key });
|
||||||
} else {
|
} else {
|
||||||
ContentValues data = new ContentValues();
|
ContentValues data = new ContentValues();
|
||||||
data.put("key", key);
|
data.put("key", key);
|
||||||
data.put("value", value);
|
data.put("value", value);
|
||||||
mDb.replace(STRINGS_TABLE, null, data);
|
db.replace(STRINGS_TABLE, null, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStrings(String key, String defaultValue) {
|
public String getStrings(String key, String defaultValue) {
|
||||||
String value = defaultValue;
|
String value = defaultValue;
|
||||||
try (Cursor c = mDb.query(STRINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
try (Cursor c = db.query(STRINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
||||||
if (c.moveToNext()) {
|
if (c.moveToNext()) {
|
||||||
value = c.getString(c.getColumnIndex("value"));
|
value = c.getString(c.getColumnIndex("value"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() {
|
|
||||||
mDb.close();
|
|
||||||
mDb = SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -10,8 +10,8 @@ import com.topjohnwu.magisk.container.Repo;
|
|||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
@@ -26,9 +26,9 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
mm = Utils.getMagiskManager(context);
|
mm = Utils.getMagiskManager(context);
|
||||||
mDb = getWritableDatabase();
|
mDb = getWritableDatabase();
|
||||||
|
|
||||||
// Clear bad repos
|
// Remove outdated repos
|
||||||
mDb.delete(TABLE_NAME, "minMagisk<?",
|
mDb.delete(TABLE_NAME, "minMagisk<?",
|
||||||
new String[] { String.valueOf(Const.MIN_MODULE_VER) });
|
new String[] { String.valueOf(Const.MIN_MODULE_VER()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -74,7 +74,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
|
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeRepo(List<String> list) {
|
public void removeRepo(Iterable<String> list) {
|
||||||
for (String id : list) {
|
for (String id : list) {
|
||||||
if (id == null) continue;
|
if (id == null) continue;
|
||||||
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
||||||
@@ -94,6 +94,10 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cursor getRawCursor() {
|
||||||
|
return mDb.query(TABLE_NAME, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public Cursor getRepoCursor() {
|
public Cursor getRepoCursor() {
|
||||||
String orderBy = null;
|
String orderBy = null;
|
||||||
switch (mm.repoOrder) {
|
switch (mm.repoOrder) {
|
||||||
@@ -103,18 +107,18 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
case Const.Value.ORDER_DATE:
|
case Const.Value.ORDER_DATE:
|
||||||
orderBy = "last_update DESC";
|
orderBy = "last_update DESC";
|
||||||
}
|
}
|
||||||
return mDb.query(TABLE_NAME, null, "minMagisk<=?",
|
return mDb.query(TABLE_NAME, null, "minMagisk<=? AND minMagisk>=?",
|
||||||
new String[] { String.valueOf(mm.magiskVersionCode) },
|
new String[] { String.valueOf(mm.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER()) },
|
||||||
null, null, orderBy);
|
null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getRepoIDList() {
|
public Set<String> getRepoIDSet() {
|
||||||
LinkedList<String> ret = new LinkedList<>();
|
HashSet<String> set = new HashSet<>(300);
|
||||||
try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) {
|
try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
ret.add(c.getString(c.getColumnIndex("id")));
|
set.add(c.getString(c.getColumnIndex("id")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return set;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,7 +9,8 @@ import com.topjohnwu.magisk.services.OnBootIntentService;
|
|||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
public class BootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private void startIntentService(Context context) {
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
context.startForegroundService(new Intent(context, OnBootIntentService.class));
|
context.startForegroundService(new Intent(context, OnBootIntentService.class));
|
||||||
} else {
|
} else {
|
||||||
@@ -17,9 +18,4 @@ public class BootReceiver extends BroadcastReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
startIntentService(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
package com.topjohnwu.magisk.receivers;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.PatchAPK;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
public class ManagerUpdate extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Utils.dlAndReceive(
|
||||||
|
context, new PatchedInstall(),
|
||||||
|
intent.getStringExtra(Const.Key.INTENT_SET_LINK),
|
||||||
|
intent.getStringExtra(Const.Key.INTENT_SET_FILENAME)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PatchedInstall extends ManagerInstall {
|
||||||
|
@Override
|
||||||
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
|
if (!context.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
String o = uri.getPath();
|
||||||
|
String p = o.substring(0, o.lastIndexOf('.')) + "-patched.apk";
|
||||||
|
try {
|
||||||
|
PatchAPK.patchPackageID(o, new BufferedOutputStream(new FileOutputStream(p)),
|
||||||
|
Const.ORIG_PKG_NAME, context.getPackageName());
|
||||||
|
} catch (FileNotFoundException ignored) { }
|
||||||
|
super.onDownloadDone(context, Uri.fromFile(new File(p)));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
super.onDownloadDone(context, uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -20,11 +20,11 @@ public class PackageReceiver extends BroadcastReceiver {
|
|||||||
case Intent.ACTION_PACKAGE_REPLACED:
|
case Intent.ACTION_PACKAGE_REPLACED:
|
||||||
// This will only work pre-O
|
// This will only work pre-O
|
||||||
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
|
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
|
||||||
mm.suDB.deletePolicy(pkg);
|
mm.mDB.deletePolicy(pkg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
||||||
mm.suDB.deletePolicy(pkg);
|
mm.mDB.deletePolicy(pkg);
|
||||||
Shell.Async.su("magiskhide --rm " + pkg);
|
Shell.Async.su("magiskhide --rm " + pkg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
@@ -0,0 +1,87 @@
|
|||||||
|
package com.topjohnwu.magisk.receivers;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.content.pm.ShortcutManager;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ShortcutReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
MagiskManager mm = Utils.getMagiskManager(context);
|
||||||
|
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
|
||||||
|
if (TextUtils.equals(intent.getAction(), Intent.ACTION_LOCALE_CHANGED)) {
|
||||||
|
// It is triggered with locale change, manual load Magisk info
|
||||||
|
mm.loadMagiskInfo();
|
||||||
|
}
|
||||||
|
manager.setDynamicShortcuts(getShortCuts(mm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
|
||||||
|
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
|
||||||
|
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
|
||||||
|
if (Shell.rootAccess() &&
|
||||||
|
!(Const.USER_ID > 0 &&
|
||||||
|
mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
||||||
|
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
|
||||||
|
.setShortLabel(mm.getString(R.string.superuser))
|
||||||
|
.setIntent(new Intent(mm, SplashActivity.class)
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "superuser")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(mm, R.drawable.sc_superuser))
|
||||||
|
.setRank(0)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (Shell.rootAccess() && mm.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
|
||||||
|
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
|
||||||
|
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
|
||||||
|
.setShortLabel(mm.getString(R.string.magiskhide))
|
||||||
|
.setIntent(new Intent(mm, SplashActivity.class)
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(mm, R.drawable.sc_magiskhide))
|
||||||
|
.setRank(1)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) &&
|
||||||
|
Shell.rootAccess() && mm.magiskVersionCode >= 0) {
|
||||||
|
shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
|
||||||
|
.setShortLabel(mm.getString(R.string.modules))
|
||||||
|
.setIntent(new Intent(mm, SplashActivity.class)
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "modules")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(mm, R.drawable.sc_extension))
|
||||||
|
.setRank(3)
|
||||||
|
.build());
|
||||||
|
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
|
||||||
|
.setShortLabel(mm.getString(R.string.download))
|
||||||
|
.setIntent(new Intent(mm, SplashActivity.class)
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "downloads")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(mm, R.drawable.sc_cloud_download))
|
||||||
|
.setRank(2)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return shortCuts;
|
||||||
|
}
|
||||||
|
}
|
@@ -8,8 +8,7 @@ import android.support.v4.app.NotificationCompat;
|
|||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
public class OnBootIntentService extends IntentService {
|
public class OnBootIntentService extends IntentService {
|
||||||
|
|
||||||
@@ -21,12 +20,12 @@ public class OnBootIntentService extends IntentService {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationCompat.Builder builder =
|
startForeground(Const.ID.ONBOOT_NOTIFICATION_ID,
|
||||||
new NotificationCompat.Builder(this, Const.ID.NOTIFICATION_CHANNEL);
|
new NotificationCompat.Builder(this, Const.ID.NOTIFICATION_CHANNEL)
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle("onBoot")
|
.setContentTitle("Startup Operations")
|
||||||
.setContentText("Running onBoot operations...");
|
.setContentText("Running startup operations...")
|
||||||
startForeground(Const.ID.ONBOOT_NOTIFICATION_ID, builder.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +38,7 @@ public class OnBootIntentService extends IntentService {
|
|||||||
* Check for dtbo status every boot time, and prompt user
|
* Check for dtbo status every boot time, and prompt user
|
||||||
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
||||||
* */
|
* */
|
||||||
MagiskManager mm = Utils.getMagiskManager(this);
|
MagiskManager.get().loadMagiskInfo();
|
||||||
mm.loadMagiskInfo();
|
RootUtils.patchDTBO();
|
||||||
mm.getDefaultInstallFlags();
|
|
||||||
if (Shell.rootAccess()) {
|
|
||||||
Utils.patchDTBO();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -68,7 +68,7 @@ public class RequestActivity extends Activity {
|
|||||||
|
|
||||||
pm = getPackageManager();
|
pm = getPackageManager();
|
||||||
mm = Utils.getMagiskManager(this);
|
mm = Utils.getMagiskManager(this);
|
||||||
mm.suDB.clearOutdated();
|
mm.mDB.clearOutdated();
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
socketPath = intent.getStringExtra("socket");
|
socketPath = intent.getStringExtra("socket");
|
||||||
@@ -233,7 +233,7 @@ public class RequestActivity extends Activity {
|
|||||||
policy.policy = action;
|
policy.policy = action;
|
||||||
if (time >= 0) {
|
if (time >= 0) {
|
||||||
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
|
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
|
||||||
mm.suDB.addPolicy(policy);
|
mm.mDB.addPolicy(policy);
|
||||||
}
|
}
|
||||||
handleAction();
|
handleAction();
|
||||||
}
|
}
|
||||||
@@ -273,10 +273,14 @@ public class RequestActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int uid = payload.getAsInteger("uid");
|
int uid = payload.getAsInteger("uid");
|
||||||
policy = mm.suDB.getPolicy(uid);
|
policy = mm.mDB.getPolicy(uid);
|
||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
policy = new Policy(uid, pm);
|
policy = new Policy(uid, pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Never allow com.topjohnwu.magisk (could be malware) */
|
||||||
|
if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME))
|
||||||
|
return false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
@@ -43,7 +43,7 @@ public class SuReceiver extends BroadcastReceiver {
|
|||||||
action = intent.getStringExtra("action");
|
action = intent.getStringExtra("action");
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
|
|
||||||
policy = mm.suDB.getPolicy(fromUid);
|
policy = mm.mDB.getPolicy(fromUid);
|
||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
try {
|
try {
|
||||||
policy = new Policy(fromUid, context.getPackageManager());
|
policy = new Policy(fromUid, context.getPackageManager());
|
||||||
@@ -84,7 +84,7 @@ public class SuReceiver extends BroadcastReceiver {
|
|||||||
log.fromPid = pid;
|
log.fromPid = pid;
|
||||||
log.command = command;
|
log.command = command;
|
||||||
log.date = new Date();
|
log.date = new Date();
|
||||||
mm.suDB.addLog(log);
|
mm.mDB.addLog(log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,22 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.support.annotation.Keep;
|
||||||
|
|
||||||
|
public interface ISafetyNetHelper {
|
||||||
|
|
||||||
|
int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
||||||
|
int CAUSE_NETWORK_LOST = 0x02;
|
||||||
|
int RESPONSE_ERR = 0x04;
|
||||||
|
int CONNECTION_FAIL = 0x08;
|
||||||
|
|
||||||
|
int BASIC_PASS = 0x10;
|
||||||
|
int CTS_PASS = 0x20;
|
||||||
|
|
||||||
|
void attest();
|
||||||
|
int getVersion();
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
@Keep
|
||||||
|
void onResponse(int responseCode);
|
||||||
|
}
|
||||||
|
}
|
27
src/full/java/com/topjohnwu/magisk/utils/Logger.java
Normal file
27
src/full/java/com/topjohnwu/magisk/utils/Logger.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class Logger {
|
||||||
|
|
||||||
|
public static void debug(String line) {
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(Const.DEBUG_TAG, "DEBUG: " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void debug(String fmt, Object... args) {
|
||||||
|
debug(Utils.fmt(fmt, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(String line) {
|
||||||
|
Log.e(Const.DEBUG_TAG, "ERROR: " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(String fmt, Object... args) {
|
||||||
|
error(Utils.fmt(fmt, args));
|
||||||
|
}
|
||||||
|
}
|
77
src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java
Normal file
77
src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import com.topjohnwu.utils.JarMap;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
|
||||||
|
public class PatchAPK {
|
||||||
|
|
||||||
|
private static int findOffset(byte buf[], byte pattern[]) {
|
||||||
|
int offset = -1;
|
||||||
|
for (int i = 0; i < buf.length - pattern.length; ++i) {
|
||||||
|
boolean match = true;
|
||||||
|
for (int j = 0; j < pattern.length; ++j) {
|
||||||
|
if (buf[i + j] != pattern[j]) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
offset = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It seems that AAPT sometimes generate another type of string format */
|
||||||
|
private static boolean fallbackPatch(byte xml[], String from, String to) {
|
||||||
|
|
||||||
|
byte[] target = new byte[from.length() * 2 + 2];
|
||||||
|
for (int i = 0; i < from.length(); ++i) {
|
||||||
|
target[i * 2] = (byte) from.charAt(i);
|
||||||
|
}
|
||||||
|
int offset = findOffset(xml, target);
|
||||||
|
if (offset < 0)
|
||||||
|
return false;
|
||||||
|
byte[] dest = new byte[target.length - 2];
|
||||||
|
for (int i = 0; i < to.length(); ++i) {
|
||||||
|
dest[i * 2] = (byte) to.charAt(i);
|
||||||
|
}
|
||||||
|
System.arraycopy(dest, 0, xml, offset, dest.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean findAndPatch(byte xml[], String from, String to) {
|
||||||
|
byte target[] = (from + '\0').getBytes();
|
||||||
|
int offset = findOffset(xml, target);
|
||||||
|
if (offset < 0)
|
||||||
|
return fallbackPatch(xml, from, to);
|
||||||
|
System.arraycopy(to.getBytes(), 0, xml, offset, to.length());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean patchPackageID(String fileName, OutputStream out, String from, String to) {
|
||||||
|
try {
|
||||||
|
JarMap apk = new JarMap(fileName);
|
||||||
|
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
|
||||||
|
byte xml[] = apk.getRawData(je);
|
||||||
|
|
||||||
|
if (!findAndPatch(xml, from, to))
|
||||||
|
return false;
|
||||||
|
if (!findAndPatch(xml, from + ".provider", to + ".provider"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Write in changes
|
||||||
|
apk.getOutputStream(je).write(xml);
|
||||||
|
|
||||||
|
// Sign the APK
|
||||||
|
ZipUtils.signZip(apk, out);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
38
src/full/java/com/topjohnwu/magisk/utils/RootUtils.java
Normal file
38
src/full/java/com/topjohnwu/magisk/utils/RootUtils.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
|
public class RootUtils {
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
if (Shell.rootAccess()) {
|
||||||
|
Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
|
||||||
|
SuFile file = new SuFile("/sbin/.core/img");
|
||||||
|
if (file.exists()) {
|
||||||
|
Const.MAGISK_PATH = file;
|
||||||
|
} else if ((file = new SuFile("/dev/magisk/img")).exists()) {
|
||||||
|
Const.MAGISK_PATH = file;
|
||||||
|
} else {
|
||||||
|
Const.MAGISK_PATH = new SuFile("/magisk");
|
||||||
|
}
|
||||||
|
Const.MAGISK_HOST_FILE = new SuFile(Const.MAGISK_PATH + "/.core/hosts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uninstallPkg(String pkg) {
|
||||||
|
Shell.Sync.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void patchDTBO() {
|
||||||
|
if (Shell.rootAccess()) {
|
||||||
|
MagiskManager mm = MagiskManager.get();
|
||||||
|
if (mm.magiskVersionCode >= Const.MAGISK_VER.DTBO_SUPPORT) {
|
||||||
|
if (Boolean.parseBoolean(ShellUtils.fastCmd("mm_patch_dtbo")))
|
||||||
|
ShowUI.dtboPatchedNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.superuser.BusyBox;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class ShellInitializer extends Shell.Initializer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onRootShellInit(Context context, @NonNull Shell shell) throws Exception {
|
||||||
|
BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
|
||||||
|
try (InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions);
|
||||||
|
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils)
|
||||||
|
) {
|
||||||
|
shell.loadInputStream(null, null, magiskUtils);
|
||||||
|
shell.loadInputStream(null, null, managerUtils);
|
||||||
|
}
|
||||||
|
shell.run(null, null,
|
||||||
|
"mount_partitions",
|
||||||
|
"get_flags",
|
||||||
|
"run_migrations");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -7,27 +7,26 @@ 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.net.Uri;
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.SplashActivity;
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
import com.topjohnwu.magisk.asyncs.RestoreImages;
|
import com.topjohnwu.magisk.asyncs.RestoreImages;
|
||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
||||||
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -45,7 +44,7 @@ public class ShowUI {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle(mm.getString(R.string.magisk_update_title))
|
.setContentTitle(mm.getString(R.string.magisk_update_title))
|
||||||
.setContentText(mm.getString(R.string.magisk_update_available, mm.remoteMagiskVersionString))
|
.setContentText(mm.getString(R.string.magisk_update_available, mm.remoteMagiskVersionString))
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
@@ -69,7 +68,7 @@ public class ShowUI {
|
|||||||
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle(mm.getString(R.string.manager_update_title))
|
.setContentTitle(mm.getString(R.string.manager_update_title))
|
||||||
.setContentText(mm.getString(R.string.manager_download_install))
|
.setContentText(mm.getString(R.string.manager_download_install))
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
@@ -89,7 +88,7 @@ public class ShowUI {
|
|||||||
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
|
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
|
||||||
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
|
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
@@ -100,11 +99,31 @@ public class ShowUI {
|
|||||||
notificationManager.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
|
notificationManager.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void magiskInstallDialog(Activity activity) {
|
public static void envFixDialog(Activity activity) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
||||||
new AlertDialogBuilder(activity)
|
new AlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.env_fix_title)
|
||||||
|
.setMessage(R.string.env_fix_msg)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setPositiveButton(R.string.yes, (d, i) -> {
|
||||||
|
Utils.dlAndReceive(activity, new DownloadReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
|
new InstallMagisk(activity, uri).exec();
|
||||||
|
}
|
||||||
|
}, mm.magiskLink, filename);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void magiskInstallDialog(Activity activity) {
|
||||||
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
|
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
||||||
|
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
||||||
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)))
|
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)))
|
||||||
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
@@ -115,17 +134,11 @@ public class ShowUI {
|
|||||||
if (Shell.rootAccess()) {
|
if (Shell.rootAccess()) {
|
||||||
options.add(mm.getString(R.string.direct_install));
|
options.add(mm.getString(R.string.direct_install));
|
||||||
}
|
}
|
||||||
String s = Utils.cmd("echo $SLOT");
|
|
||||||
if (s != null) {
|
|
||||||
options.add(mm.getString(R.string.install_second_slot));
|
|
||||||
}
|
|
||||||
char[] slot = s == null ? null : s.toCharArray();
|
|
||||||
new AlertDialog.Builder(activity)
|
new AlertDialog.Builder(activity)
|
||||||
.setTitle(R.string.select_method)
|
.setTitle(R.string.select_method)
|
||||||
.setItems(
|
.setItems(
|
||||||
options.toArray(new String [0]),
|
options.toArray(new String [0]),
|
||||||
(dialog, idx) -> {
|
(dialog, idx) -> {
|
||||||
String boot;
|
|
||||||
DownloadReceiver receiver = null;
|
DownloadReceiver receiver = null;
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -145,7 +158,7 @@ public class ShowUI {
|
|||||||
activity,
|
activity,
|
||||||
new DownloadReceiver() {
|
new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
Intent intent = new Intent(mm, FlashActivity.class);
|
||||||
intent.setData(uri)
|
intent.setData(uri)
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
@@ -163,47 +176,29 @@ public class ShowUI {
|
|||||||
case 0:
|
case 0:
|
||||||
receiver = new DownloadReceiver() {
|
receiver = new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Utils.showUriSnack(activity, uri);
|
SnackbarMaker.showUri(activity, uri);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
boot = mm.bootBlock;
|
|
||||||
if (boot == null)
|
|
||||||
return;
|
|
||||||
receiver = new DownloadReceiver() {
|
receiver = new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
Intent intent = new Intent(mm, FlashActivity.class);
|
||||||
intent.setData(uri)
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION,
|
||||||
.putExtra(Const.Key.FLASH_SET_BOOT, boot)
|
Const.Value.FLASH_MAGISK);
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
assert (slot != null);
|
|
||||||
// Choose the other slot
|
|
||||||
if (slot[1] == 'a') slot[1] = 'b';
|
|
||||||
else slot[1] = 'a';
|
|
||||||
// Then find the boot image again
|
|
||||||
boot = Utils.cmd(
|
|
||||||
"SLOT=" + String.valueOf(slot) +
|
|
||||||
"; find_boot_image;" +
|
|
||||||
"echo \"$BOOTIMAGE\""
|
|
||||||
);
|
|
||||||
Shell.Async.su("mount_partitions");
|
|
||||||
if (boot == null)
|
|
||||||
return;
|
|
||||||
receiver = new DownloadReceiver() {
|
receiver = new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
Intent intent = new Intent(mm, FlashActivity.class);
|
||||||
intent.setData(uri)
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION,
|
||||||
.putExtra(Const.Key.FLASH_SET_BOOT, boot)
|
Const.Value.FLASH_SECOND_SLOT);
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -213,73 +208,66 @@ public class ShowUI {
|
|||||||
}
|
}
|
||||||
).show();
|
).show();
|
||||||
})
|
})
|
||||||
.setNeutralButton(R.string.release_notes, (d, i) -> {
|
.setNegativeButton(R.string.no_thanks, null);
|
||||||
if (mm.releaseNoteLink != null) {
|
if (!TextUtils.isEmpty(mm.magiskNoteLink)) {
|
||||||
Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(mm.releaseNoteLink));
|
b.setNeutralButton(R.string.release_notes, (d, i) -> {
|
||||||
|
if (mm.magiskNoteLink.contains("forum.xda-developers")) {
|
||||||
|
// Open forum links in browser
|
||||||
|
Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(mm.magiskNoteLink));
|
||||||
openLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
openLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
mm.startActivity(openLink);
|
mm.startActivity(openLink);
|
||||||
|
} else {
|
||||||
|
new MarkDownWindow(activity, null, mm.magiskNoteLink).exec();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
}
|
||||||
.show();
|
b.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void managerInstallDialog(Activity activity) {
|
public static void managerInstallDialog(Activity activity) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
||||||
mm.remoteManagerVersionString, mm.remoteManagerVersionCode);
|
mm.remoteManagerVersionString, mm.remoteManagerVersionCode);
|
||||||
new AlertDialogBuilder(activity)
|
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
||||||
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)))
|
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)))
|
||||||
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setPositiveButton(R.string.install, (d, i) -> {
|
.setPositiveButton(R.string.install, (d, i) -> {
|
||||||
Utils.runWithPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
|
com.topjohnwu.magisk.components.Activity.runWithPermission(activity,
|
||||||
|
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
Intent intent = new Intent(mm, ManagerUpdate.class);
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
||||||
mm.sendBroadcast(intent);
|
mm.sendBroadcast(intent);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
.setNegativeButton(R.string.no_thanks, null);
|
||||||
.show();
|
if (!TextUtils.isEmpty(mm.managerNoteLink)) {
|
||||||
|
b.setNeutralButton(R.string.app_changelog, (d, i) ->
|
||||||
|
new MarkDownWindow(activity, null, mm.managerNoteLink).exec());
|
||||||
|
}
|
||||||
|
b.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void uninstallDialog(Activity activity) {
|
public static void uninstallDialog(Activity activity) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
new AlertDialogBuilder(activity)
|
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
||||||
.setTitle(R.string.uninstall_magisk_title)
|
.setTitle(R.string.uninstall_magisk_title)
|
||||||
.setMessage(R.string.uninstall_magisk_msg)
|
.setMessage(R.string.uninstall_magisk_msg)
|
||||||
.setPositiveButton(R.string.complete_uninstall, (d, i) -> {
|
.setNeutralButton(R.string.restore_img, (d, i) -> new RestoreImages(activity).exec());
|
||||||
ByteArrayOutputStream uninstaller = new ByteArrayOutputStream();
|
if (!TextUtils.isEmpty(mm.uninstallerLink)) {
|
||||||
try (InputStream in = mm.getAssets().open(Const.UNINSTALLER)) {
|
b.setPositiveButton(R.string.complete_uninstall, (d, i) ->
|
||||||
ShellUtils.pump(in, uninstaller);
|
Utils.dlAndReceive(activity, new DownloadReceiver() {
|
||||||
} catch (IOException e) {
|
@Override
|
||||||
e.printStackTrace();
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
return;
|
Intent intent = new Intent(context, FlashActivity.class)
|
||||||
}
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
ByteArrayOutputStream utils = new ByteArrayOutputStream();
|
.setData(uri)
|
||||||
try (InputStream in = mm.getAssets().open(Const.UTIL_FUNCTIONS)) {
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
||||||
ShellUtils.pump(in, utils);
|
context.startActivity(intent);
|
||||||
} catch (IOException e) {
|
}
|
||||||
e.printStackTrace();
|
}, mm.uninstallerLink, "magisk-uninstaller.zip"));
|
||||||
return;
|
}
|
||||||
}
|
b.show();
|
||||||
|
|
||||||
Shell.Sync.su(
|
|
||||||
Utils.fmt("echo '%s' > /cache/%s", uninstaller.toString().replace("'", "'\\''"), Const.UNINSTALLER),
|
|
||||||
Utils.fmt("echo '%s' > %s/%s", utils.toString().replace("'", "'\\''"),
|
|
||||||
mm.magiskVersionCode >= 1464 ? "/data/adb/magisk" : "/data/magisk", Const.UTIL_FUNCTIONS)
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
uninstaller.close();
|
|
||||||
utils.close();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
|
|
||||||
MagiskManager.toast(R.string.uninstall_toast, Toast.LENGTH_LONG);
|
|
||||||
new Handler().postDelayed(() -> Utils.uninstallPkg(mm.getPackageName()), 5000);
|
|
||||||
})
|
|
||||||
.setNeutralButton(R.string.restore_img, (d, i) -> new RestoreImages().exec())
|
|
||||||
.setNegativeButton(R.string.uninstall_app, (d, i) -> Utils.uninstallPkg(mm.getPackageName()))
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
99
src/full/java/com/topjohnwu/magisk/utils/Topic.java
Normal file
99
src/full/java/com/topjohnwu/magisk/utils/Topic.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Topic {
|
||||||
|
|
||||||
|
private static final int NON_INIT = 0;
|
||||||
|
private static final int PENDING = 1;
|
||||||
|
private static final int PUBLISHED = 2;
|
||||||
|
|
||||||
|
private int state = NON_INIT;
|
||||||
|
private List<WeakReference<Subscriber>> subscribers;
|
||||||
|
private Object[] results;
|
||||||
|
|
||||||
|
public Topic() {
|
||||||
|
subscribers = new SyncArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void subscribe(Subscriber sub) {
|
||||||
|
subscribers.add(new WeakReference<>(sub));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unsubscribe() {
|
||||||
|
subscribers = new SyncArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unsubscribe(Subscriber sub) {
|
||||||
|
List<WeakReference<Subscriber>> subs = subscribers;
|
||||||
|
subscribers = new ArrayList<>();
|
||||||
|
for (WeakReference<Subscriber> subscriber : subs) {
|
||||||
|
if (subscriber.get() != null && subscriber.get() != sub)
|
||||||
|
subscribers.add(subscriber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
state = NON_INIT;
|
||||||
|
results = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPublished() {
|
||||||
|
return state == PUBLISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publish() {
|
||||||
|
publish(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publish(boolean record, Object... results) {
|
||||||
|
if (record)
|
||||||
|
state = PUBLISHED;
|
||||||
|
this.results = results;
|
||||||
|
// Snapshot
|
||||||
|
List<WeakReference<Subscriber>> subs = subscribers;
|
||||||
|
for (WeakReference<Subscriber> subscriber : subs) {
|
||||||
|
if (subscriber != null && subscriber.get() != null)
|
||||||
|
subscriber.get().onTopicPublished(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getResults() {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPending() {
|
||||||
|
return state == PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPending() {
|
||||||
|
state = PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Subscriber {
|
||||||
|
default void subscribeTopics() {
|
||||||
|
for (Topic topic : getSubscription()) {
|
||||||
|
if (topic.isPublished()) {
|
||||||
|
onTopicPublished(topic);
|
||||||
|
}
|
||||||
|
topic.subscribe(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default void unsubscribeTopics() {
|
||||||
|
for (Topic event : getSubscription()) {
|
||||||
|
event.unsubscribe(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void onTopicPublished(Topic topic);
|
||||||
|
Topic[] getSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SyncArrayList<E> extends ArrayList<E> {
|
||||||
|
@Override
|
||||||
|
public synchronized boolean add(E e) {
|
||||||
|
return super.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
||||||
import com.topjohnwu.utils.JarMap;
|
import com.topjohnwu.utils.JarMap;
|
||||||
import com.topjohnwu.utils.SignAPK;
|
import com.topjohnwu.utils.SignAPK;
|
||||||
|
|
||||||
@@ -9,6 +11,7 @@ import java.io.BufferedOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
@@ -16,13 +19,13 @@ import java.util.zip.ZipInputStream;
|
|||||||
|
|
||||||
public class ZipUtils {
|
public class ZipUtils {
|
||||||
|
|
||||||
public static void unzip(File zip, File folder, String path, boolean junkPath) throws Exception {
|
public static void unzip(File zip, File folder, String path, boolean junkPath) throws IOException {
|
||||||
InputStream in = new BufferedInputStream(new FileInputStream(zip));
|
InputStream in = new BufferedInputStream(new FileInputStream(zip));
|
||||||
unzip(in, folder, path, junkPath);
|
unzip(in, folder, path, junkPath);
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws Exception {
|
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws IOException {
|
||||||
try {
|
try {
|
||||||
ZipInputStream zipfile = new ZipInputStream(zip);
|
ZipInputStream zipfile = new ZipInputStream(zip);
|
||||||
ZipEntry entry;
|
ZipEntry entry;
|
||||||
@@ -38,32 +41,25 @@ public class ZipUtils {
|
|||||||
name = entry.getName();
|
name = entry.getName();
|
||||||
}
|
}
|
||||||
File dest = new File(folder, name);
|
File dest = new File(folder, name);
|
||||||
dest.getParentFile().mkdirs();
|
if (!dest.getParentFile().exists() && !dest.getParentFile().mkdirs()) {
|
||||||
try (FileOutputStream out = new FileOutputStream(dest)) {
|
dest = new SuFile(folder, name);
|
||||||
|
dest.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
try (OutputStream out = new SuFileOutputStream(dest)) {
|
||||||
ShellUtils.pump(zipfile, out);
|
ShellUtils.pump(zipfile, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
}
|
||||||
|
catch(IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void signZip(InputStream is, File output) throws Exception {
|
|
||||||
try (JarMap map = new JarMap(is, false)) {
|
|
||||||
signZip(map, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signZip(File input, File output) throws Exception {
|
public static void signZip(File input, File output) throws Exception {
|
||||||
try (JarMap map = new JarMap(input, false)) {
|
try (JarMap map = new JarMap(input, false);
|
||||||
signZip(map, output);
|
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
|
||||||
}
|
signZip(map, out);
|
||||||
}
|
|
||||||
|
|
||||||
public static void signZip(JarMap input, File output) throws Exception {
|
|
||||||
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
|
|
||||||
signZip(input, out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
src/full/res/drawable-nodpi/logo.png
Normal file
BIN
src/full/res/drawable-nodpi/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
9
src/full/res/drawable-v26/sc_cloud_download.xml
Normal file
9
src/full/res/drawable-v26/sc_cloud_download.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/su_request_background" />
|
||||||
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_cloud_download"
|
||||||
|
android:inset="30%" />
|
||||||
|
</foreground>
|
||||||
|
</adaptive-icon>
|
9
src/full/res/drawable-v26/sc_extension.xml
Normal file
9
src/full/res/drawable-v26/sc_extension.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/su_request_background" />
|
||||||
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_extension"
|
||||||
|
android:inset="30%" />
|
||||||
|
</foreground>
|
||||||
|
</adaptive-icon>
|
9
src/full/res/drawable-v26/sc_magiskhide.xml
Normal file
9
src/full/res/drawable-v26/sc_magiskhide.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/su_request_background" />
|
||||||
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_magiskhide"
|
||||||
|
android:inset="30%" />
|
||||||
|
</foreground>
|
||||||
|
</adaptive-icon>
|
9
src/full/res/drawable-v26/sc_superuser.xml
Normal file
9
src/full/res/drawable-v26/sc_superuser.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/su_request_background" />
|
||||||
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_superuser"
|
||||||
|
android:inset="30%" />
|
||||||
|
</foreground>
|
||||||
|
</adaptive-icon>
|
@@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="@color/primary_dark"
|
||||||
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
|
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
|
||||||
</vector>
|
</vector>
|
@@ -4,6 +4,6 @@
|
|||||||
android:viewportHeight="24.0"
|
android:viewportHeight="24.0"
|
||||||
android:viewportWidth="24.0">
|
android:viewportWidth="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="@color/primary_dark"
|
||||||
android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z"/>
|
android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z"/>
|
||||||
</vector>
|
</vector>
|
18
src/full/res/drawable/ic_magisk_outline.xml
Normal file
18
src/full/res/drawable/ic_magisk_outline.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<vector android:height="48dp" android:viewportHeight="720"
|
||||||
|
android:viewportWidth="720" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M332.48,421.18c0,0 3.77,22.45 -0.82,71.95c-5.76,62.06 23.64,160.64 23.64,160.64c0,0 40.1,-98.78 33.1,-162.59c-5.75,-52.45 2.6,-70.79 0.82,-68.33c-30.81,42.57 -56.75,-1.67 -56.75,-1.67z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M407.6,474.45c5.01,38.77 -0.57,60.01 -7.81,101.51c-3.66,20.99 74.78,-63.1 104.86,-113.23c5.02,-8.36 -28.77,32.6 -62.19,3.35c-23.18,-20.28 -27.16,-26.44 -45.18,-44.06c-6.08,-5.94 6.74,24.72 10.32,52.43z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M321.99,425.09c-18.02,17.62 -22,23.78 -45.18,44.06c-33.42,29.25 -67.21,-11.71 -62.19,-3.35c30.08,50.13 108.52,134.22 104.86,113.23c-7.24,-41.5 -12.82,-62.74 -7.81,-101.51c3.58,-27.71 16.4,-58.37 10.32,-52.43z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M399.15,355.87c36.67,10.57 50.89,61.5 87.91,67.8c7.65,1.3 16.27,3.6 26.31,3.12c18.77,-0.9 42.51,-11.51 74.22,-56.5c9.38,-13.3 -23.27,85.66 -105.13,86.86c-59.96,0.88 -66.97,-58.7 -106.93,-60.51c-14.43,-0.65 -15.34,-28.17 -15.34,-28.17c0,0 17.22,-18.86 38.96,-12.6z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M321.51,355.59c-36.67,10.57 -50.89,61.5 -87.91,67.8c-7.65,1.3 -16.27,3.6 -26.31,3.12c-18.77,-0.9 -42.51,-11.51 -74.22,-56.5c-9.38,-13.3 23.27,85.66 105.13,86.86c59.96,0.88 66.97,-58.7 106.93,-60.51c14.43,-0.65 15.34,-28.17 15.34,-28.17c0,0 -17.22,-18.86 -38.96,-12.6z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M458.64,355.09c36.87,27.94 25.88,58.7 46.57,49.92c69.7,-29.55 57.51,-181.21 51.87,-162.87c-31.77,103.41 -100.99,109.2 -167.61,61.63c-13.01,-9.29 48.38,35.57 69.16,51.31z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M330.91,303.77c-66.62,47.56 -135.84,41.78 -167.61,-61.63c-5.63,-18.34 -17.82,133.31 51.87,162.87c20.7,8.78 9.7,-21.98 46.57,-49.92c20.78,-15.75 82.17,-60.6 69.16,-51.31z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M465.61,318c80.43,-3.32 95.29,-135.17 88.96,-119.08c-28.39,72.22 -135.86,45.05 -146.13,90.64c-2.02,8.94 18.2,30.06 57.17,28.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M311.95,289.55c-10.27,-45.59 -117.75,-18.41 -146.13,-90.64c-6.32,-16.09 8.53,115.76 88.96,119.08c38.97,1.61 59.19,-19.5 57.17,-28.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M403.42,269.47c0,0 43.73,-23.5 81.16,-33.74c34.99,-9.58 61.22,-33.13 64.14,-58.01c2.18,-18.53 -27.05,-53.55 -27.05,-53.55c0,0 -20.51,56.9 -47.41,85.34c-29.28,30.96 -18.15,26.78 -70.84,59.96z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M246.13,209.51c-26.9,-28.44 -47.41,-85.34 -47.41,-85.34c0,0 -29.23,35.01 -27.05,53.55c2.93,24.88 29.16,48.43 64.14,58.01c37.43,10.25 81.16,33.74 81.16,33.74c-52.69,-33.18 -41.55,-29 -70.84,-59.96z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M398.12,265.85c47.36,-38.85 72.53,-89.54 113.51,-145.02c7.73,-10.46 -34.58,-35.7 -51.31,-37.37c-16.73,-1.67 -30.77,59.79 -32.35,95.94c-1.44,33.01 -36.21,91.68 -29.84,86.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M292.42,179.39c-1.58,-36.15 -15.62,-97.61 -32.35,-95.94c-16.73,1.67 -59.04,26.91 -51.31,37.37c40.98,55.48 66.14,106.17 113.51,145.02c6.37,5.22 -28.4,-53.45 -29.84,-86.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M402.86,140.35c3.34,-26.76 15.37,-46.32 39.32,-62.75c-21.17,-7.08 -38.77,-12.83 -47.97,-5.3c-9.2,7.53 -34.2,32.7 -30.85,73.68c3.34,40.98 0.18,194.09 7.43,191.25c3.9,-104.87 37.09,-135 32.07,-196.89z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M349.59,337.24c7.24,2.83 4.08,-150.27 7.43,-191.25c3.34,-40.98 -21.65,-66.16 -30.85,-73.68c-9.2,-7.53 -26.8,-1.78 -47.97,5.3c23.95,16.43 35.98,35.98 39.32,62.75c-5.02,61.89 28.17,92.02 32.07,196.89z"/>
|
||||||
|
</vector>
|
10
src/full/res/drawable/ic_magiskhide.xml
Normal file
10
src/full/res/drawable/ic_magiskhide.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="400.0"
|
||||||
|
android:viewportWidth="400.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillAlpha="1.00"
|
||||||
|
android:fillColor="@color/primary_dark"
|
||||||
|
android:pathData="M200.6,24.2C231.8,24.2 263,33 289.6,49.5C304.7,58.8 318.2,70.7 329.8,84.1C351.5,109.6 365.2,141.7 368.8,175C373.6,217.4 361.5,261.5 335.5,295.5C334.2,297.1 332.8,298.7 331.9,300.7C341.7,310.1 351,320 360.9,329.3C322.7,339.3 284.5,349.6 246.3,359.9C256.4,323.5 266,287 275.7,250.5C262.4,250.5 249.1,250.5 235.8,250.5C228.5,269.8 221.2,289.1 214.1,308.4C205.9,308.6 197.8,308.5 189.6,308.5C188.4,306.7 187.1,304.9 185.9,303C192.4,285.5 199.1,268 205.6,250.5C189.5,250.6 173.4,250.3 157.4,250.6C150.4,270 142.9,289.2 135.8,308.5C129.8,308.5 123.9,308.4 117.9,308.6C130.4,317.8 144,325.6 158.9,330.3C171.9,334.6 185.6,336.6 199.4,336.7C199.4,349.4 199.3,362.1 199.4,374.8C165.8,374.9 132.2,364.5 104.4,345.6C91.7,337 80.3,326.5 70.2,314.9C48.5,289.4 34.8,257.3 31.2,224C26.3,180.4 39.2,134.9 66.8,100.7C67.2,99.9 67.7,99.2 68.1,98.4C58.3,88.9 49,79 39.1,69.7C77.3,59.7 115.6,49.4 153.7,39.1C143.6,75.7 133.8,112.5 124.1,149.3C137.9,149.3 151.7,149.2 165.5,149.3C172.9,130 180.2,110.6 187.3,91.2C195.5,90.8 203.7,91 211.8,91C213,92.8 214.3,94.6 215.5,96.3C209.1,114 202.3,131.6 195.8,149.2C211.8,149.3 227.8,149.2 243.9,149.3C251.2,129.9 258.3,110.3 265.8,90.9C271.5,90.8 277.3,91.6 282.9,90.4C280.2,89.5 278.1,87.7 275.9,86.1C254,70.7 227.4,62.2 200.6,62.3C200.6,49.6 200.7,36.9 200.6,24.2M292.5,100C286.4,116.4 280.2,132.8 274,149.3C280.9,149.2 287.8,149.3 294.7,149.2C296,150.8 297.3,152.4 298.6,153.9C297.1,162.1 295.7,170.3 294.3,178.5C283.9,178.5 273.4,178.5 262.9,178.5C257.5,192.7 252.1,206.9 246.9,221.2C258.7,221.3 270.5,221.1 282.3,221.3C283.5,222.9 284.8,224.4 286.1,225.9C284.9,233.7 283.4,241.4 282.1,249.1C281.6,251 283.6,252.1 284.6,253.3C291.5,259.8 297.7,266.9 304.8,273.1C312.9,262.1 319.6,250.1 324.2,237.3C334.3,208.7 334.2,176.7 323.7,148.3C317,130.1 306.4,113.4 292.5,100M95.2,125.9C86.2,138 79,151.4 74.5,165.8C65.3,194.4 66.4,226.3 77.6,254.2C84.6,271.5 95.1,287.4 108.7,300.1C114.9,283.6 121.3,267.1 127.2,250.5C121.5,250.5 115.8,250.5 110,250.5C108.6,250.4 106.7,251 105.8,249.5C104.5,247.9 102.3,246.3 102.9,244.1C104.2,236.5 105.5,228.9 106.8,221.2C117.4,221.2 128.1,221.4 138.7,221.1C143.9,206.9 149.3,192.7 154.6,178.5C142.9,178.4 131.1,178.6 119.3,178.4C117.6,177.3 116.4,175.3 115,173.8C116.3,165.8 117.9,157.9 119,150C118.3,148.2 116.6,147.1 115.3,145.7C108.6,139.2 102.3,132.1 95.2,125.9M168.8,221.2C184.8,221.2 200.8,221.3 216.8,221.2C222.2,207 227.5,192.8 232.8,178.5C216.8,178.5 200.7,178.5 184.6,178.5C179.4,192.8 174,207 168.8,221.2Z"/>
|
||||||
|
</vector>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user