mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-11-02 14:12:29 +01:00 
			
		
		
		
	Compare commits
	
		
			121 Commits
		
	
	
		
			manager-v5
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | ||
| 
						 | 
					c840a30c30 | ||
| 
						 | 
					ae5277a898 | ||
| 
						 | 
					bffa837825 | ||
| 
						 | 
					b9e7d0faea | ||
| 
						 | 
					860b08d9ed | ||
| 
						 | 
					691dc1d49e | ||
| 
						 | 
					9d6886d367 | ||
| 
						 | 
					9589b68f5a | ||
| 
						 | 
					28d88af1af | ||
| 
						 | 
					8b5acd1849 | ||
| 
						 | 
					33dc63a7fd | ||
| 
						 | 
					d0a86385b7 | ||
| 
						 | 
					50a49e2c8c | ||
| 
						 | 
					c60adb113e | ||
| 
						 | 
					aee015e8f6 | ||
| 
						 | 
					bf6af29205 | ||
| 
						 | 
					329905d472 | ||
| 
						 | 
					00d450d262 | ||
| 
						 | 
					2365d1bd20 | ||
| 
						 | 
					5b385c18e5 | ||
| 
						 | 
					98c0434ec0 | ||
| 
						 | 
					f318d0a3bc | ||
| 
						 | 
					27f5b410c0 | ||
| 
						 | 
					3f55be9676 | ||
| 
						 | 
					b05d2d3a2d | ||
| 
						 | 
					19af5f9e0b | ||
| 
						 | 
					f37f330670 | ||
| 
						 | 
					40082d4571 | ||
| 
						 | 
					00d655f346 | ||
| 
						 | 
					821726e7c0 | ||
| 
						 | 
					759e905c3c | ||
| 
						 | 
					8bf7e42913 | ||
| 
						 | 
					0dcd073554 | ||
| 
						 | 
					2fe35d578d | ||
| 
						 | 
					8d139e156e | ||
| 
						 | 
					7c2849356a | ||
| 
						 | 
					0025ffd1c0 | ||
| 
						 | 
					2ef7146642 | ||
| 
						 | 
					1b27e69e40 | ||
| 
						 | 
					8e7b757efd | ||
| 
						 | 
					1ab543cea1 | ||
| 
						 | 
					a3f86903e4 | ||
| 
						 | 
					c239c305ab | ||
| 
						 | 
					2e02af994e | ||
| 
						 | 
					836d9afe17 | ||
| 
						 | 
					007a352742 | ||
| 
						 | 
					e526e5659e | ||
| 
						 | 
					4a5227c7bf | ||
| 
						 | 
					c2c151ec4c | ||
| 
						 | 
					452096e7e4 | ||
| 
						 | 
					50c2a9859e | ||
| 
						 | 
					677b667307 | ||
| 
						 | 
					1adf331268 | ||
| 
						 | 
					349b3e961b | ||
| 
						 | 
					96650c06f0 | ||
| 
						 | 
					26038a0a07 | ||
| 
						 | 
					6a148b5dd9 | ||
| 
						 | 
					0e109ef979 | ||
| 
						 | 
					de2285d5e9 | ||
| 
						 | 
					b2483ba437 | ||
| 
						 | 
					a82a5e5a49 | ||
| 
						 | 
					d161a02e71 | ||
| 
						 | 
					d2b6a700b1 | ||
| 
						 | 
					af203cef24 | ||
| 
						 | 
					673e917e76 | ||
| 
						 | 
					a3bd41db54 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@
 | 
			
		||||
app/release
 | 
			
		||||
*.hprof
 | 
			
		||||
.externalNativeBuild/
 | 
			
		||||
*.sh
 | 
			
		||||
src/main/assets
 | 
			
		||||
public.certificate.x509.pem
 | 
			
		||||
private.key.pk8
 | 
			
		||||
*.apk
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								build.gradle
									
									
									
									
									
								
							@@ -1,19 +1,15 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdkVersion 27
 | 
			
		||||
    buildToolsVersion "27.0.2"
 | 
			
		||||
    compileSdkVersion rootProject.ext.compileSdkVersion
 | 
			
		||||
    buildToolsVersion rootProject.ext.buildToolsVersion
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "com.topjohnwu.magisk"
 | 
			
		||||
        minSdkVersion 21
 | 
			
		||||
        targetSdkVersion 27
 | 
			
		||||
        versionCode 80
 | 
			
		||||
        versionName "5.5.0"
 | 
			
		||||
        ndk {
 | 
			
		||||
            moduleName 'zipadjust'
 | 
			
		||||
            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
 | 
			
		||||
        }
 | 
			
		||||
        targetSdkVersion rootProject.ext.compileSdkVersion
 | 
			
		||||
        versionCode 112
 | 
			
		||||
        versionName "5.6.4"
 | 
			
		||||
        javaCompileOptions {
 | 
			
		||||
            annotationProcessorOptions {
 | 
			
		||||
                argument('butterknife.debuggable', 'false')
 | 
			
		||||
@@ -25,7 +21,7 @@ android {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled true
 | 
			
		||||
            shrinkResources true
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    compileOptions {
 | 
			
		||||
@@ -36,32 +32,21 @@ android {
 | 
			
		||||
        preDexLibraries true
 | 
			
		||||
        javaMaxHeapSize "2g"
 | 
			
		||||
    }
 | 
			
		||||
    externalNativeBuild {
 | 
			
		||||
        cmake {
 | 
			
		||||
            path 'src/main/jni/CMakeLists.txt'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    lintOptions {
 | 
			
		||||
        disable 'MissingTranslation'
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    jcenter()
 | 
			
		||||
    google()
 | 
			
		||||
    maven { url "https://jitpack.io" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation fileTree(include: ['*.jar'], dir: 'libs')
 | 
			
		||||
    implementation project(':crypto')
 | 
			
		||||
    implementation 'com.android.support:recyclerview-v7:27.0.2'
 | 
			
		||||
    implementation 'com.android.support:cardview-v7:27.0.2'
 | 
			
		||||
    implementation 'com.android.support:design:27.0.2'
 | 
			
		||||
    implementation 'com.android.support:support-v4:27.0.2'
 | 
			
		||||
    implementation project(':utils')
 | 
			
		||||
    implementation 'com.github.topjohnwu:libsu:1.1.1'
 | 
			
		||||
    implementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
 | 
			
		||||
    implementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
 | 
			
		||||
    implementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
 | 
			
		||||
    implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
 | 
			
		||||
    implementation 'com.jakewharton:butterknife:8.8.1'
 | 
			
		||||
    implementation 'com.atlassian.commonmark:commonmark:0.10.0'
 | 
			
		||||
    implementation 'org.kamranzafar:jtar:2.3'
 | 
			
		||||
    implementation 'com.google.code.gson:gson:2.8.2'
 | 
			
		||||
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@@ -20,5 +20,10 @@
 | 
			
		||||
-keepnames class ** { *; }
 | 
			
		||||
 | 
			
		||||
# BouncyCastle
 | 
			
		||||
-keep class org.bouncycastle.jcajce.provider.** { *; }
 | 
			
		||||
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
 | 
			
		||||
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
 | 
			
		||||
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
 | 
			
		||||
-dontwarn javax.naming.**
 | 
			
		||||
 | 
			
		||||
# Gson
 | 
			
		||||
-keepattributes Signature
 | 
			
		||||
@@ -9,10 +9,11 @@
 | 
			
		||||
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 | 
			
		||||
    <uses-permission android:name="android.permission.VIBRATE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
 | 
			
		||||
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".MagiskManager"
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
        android:allowBackup="false"
 | 
			
		||||
        android:icon="@mipmap/ic_launcher"
 | 
			
		||||
        android:roundIcon="@mipmap/ic_launcher_round"
 | 
			
		||||
        android:label="@string/app_name"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
### v5.5.0
 | 
			
		||||
- Fix dynamic resource loading, prevent crashes when checking SafetyNet
 | 
			
		||||
- Update SignAPK to use very little RAM for supporting old devices
 | 
			
		||||
- Support settings migration after hiding Magisk Manager
 | 
			
		||||
- Add reboot menu in modules section
 | 
			
		||||
- Add dark theme to superuser request dialogs
 | 
			
		||||
- Properly handle new `HIGHCOMP` and add recommended `KEEPVERITY` and `KEEPFORCEENCRYPT` flags for installation
 | 
			
		||||
- Support new paths for v14.6
 | 
			
		||||
- Massive improvements in repackaging Magisk Manager
 | 
			
		||||
@@ -14,8 +14,6 @@ import com.topjohnwu.magisk.components.AboutCardRow;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
@@ -56,12 +54,8 @@ public class AboutActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
        appChangelog.removeSummary();
 | 
			
		||||
        appChangelog.setOnClickListener(v -> {
 | 
			
		||||
            try {
 | 
			
		||||
                InputStream is = getAssets().open("changelog.md");
 | 
			
		||||
                new MarkDownWindow(this, getString(R.string.app_changelog), is).exec();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
            new MarkDownWindow(this, getString(R.string.app_changelog),
 | 
			
		||||
                    getResources().openRawResource(R.raw.changelog)).exec();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        String translators = getString(R.string.translators);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,9 @@ import android.widget.Toast;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.FlashZip;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.InstallMagisk;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.container.CallbackList;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.superuser.CallbackList;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileWriter;
 | 
			
		||||
@@ -49,14 +49,14 @@ public class FlashActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.reboot)
 | 
			
		||||
    void reboot() {
 | 
			
		||||
        Shell.su_raw("/system/bin/reboot");
 | 
			
		||||
        Shell.Async.su("/system/bin/reboot");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.save_logs)
 | 
			
		||||
    void saveLogs() {
 | 
			
		||||
        Calendar now = Calendar.getInstance();
 | 
			
		||||
        String filename = String.format(Locale.US,
 | 
			
		||||
                "install_log_%04d%02d%02d_%02d:%02d:%02d.log",
 | 
			
		||||
                "install_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));
 | 
			
		||||
@@ -96,10 +96,10 @@ public class FlashActivity extends Activity {
 | 
			
		||||
            reboot.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
        logs = new ArrayList<>();
 | 
			
		||||
        List<String> console = new CallbackList<String>() {
 | 
			
		||||
        CallbackList<String> console = new CallbackList<String>(new ArrayList<>()) {
 | 
			
		||||
            @Override
 | 
			
		||||
            public synchronized void onAddElement(String e) {
 | 
			
		||||
                logs.add(e);
 | 
			
		||||
            public void onAddElement(String s) {
 | 
			
		||||
                logs.add(s);
 | 
			
		||||
                flashLogs.setText(TextUtils.join("\n", this));
 | 
			
		||||
                sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,10 @@ import com.topjohnwu.magisk.asyncs.CheckUpdates;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.components.ExpandableView;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ShowUI;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindColor;
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
@@ -91,7 +91,7 @@ public class MagiskFragment extends Fragment
 | 
			
		||||
            new CheckSafetyNet(getActivity()).exec();
 | 
			
		||||
            collapse();
 | 
			
		||||
        };
 | 
			
		||||
        if (mm.snetVersion < 0) {
 | 
			
		||||
        if (!CheckSafetyNet.dexPath.exists()) {
 | 
			
		||||
            // Show dialog
 | 
			
		||||
            new AlertDialogBuilder(getActivity())
 | 
			
		||||
                    .setTitle(R.string.proprietary_title)
 | 
			
		||||
@@ -138,13 +138,9 @@ public class MagiskFragment extends Fragment
 | 
			
		||||
        setupExpandable();
 | 
			
		||||
 | 
			
		||||
        keepVerityChkbox.setChecked(mm.keepVerity);
 | 
			
		||||
        keepVerityChkbox.setOnCheckedChangeListener((view, isChecked) -> {
 | 
			
		||||
            mm.keepVerity = isChecked;
 | 
			
		||||
        });
 | 
			
		||||
        keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepVerity = checked);
 | 
			
		||||
        keepEncChkbox.setChecked(mm.keepEnc);
 | 
			
		||||
        keepEncChkbox.setOnCheckedChangeListener((view, isChecked) -> {
 | 
			
		||||
            mm.keepEnc = isChecked;
 | 
			
		||||
        });
 | 
			
		||||
        keepEncChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepEnc = checked);
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(this);
 | 
			
		||||
        updateUI();
 | 
			
		||||
@@ -163,8 +159,8 @@ public class MagiskFragment extends Fragment
 | 
			
		||||
 | 
			
		||||
        safetyNetStatusText.setText(R.string.safetyNet_check_text);
 | 
			
		||||
 | 
			
		||||
        mm.safetyNetDone.hasPublished = false;
 | 
			
		||||
        mm.updateCheckDone.hasPublished = false;
 | 
			
		||||
        mm.safetyNetDone.reset();
 | 
			
		||||
        mm.updateCheckDone.reset();
 | 
			
		||||
        mm.remoteMagiskVersionString = null;
 | 
			
		||||
        mm.remoteMagiskVersionCode = -1;
 | 
			
		||||
        collapse();
 | 
			
		||||
@@ -180,11 +176,11 @@ public class MagiskFragment extends Fragment
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
    public void onTopicPublished(Topic topic) {
 | 
			
		||||
        if (topic == mm.updateCheckDone) {
 | 
			
		||||
            updateCheckUI();
 | 
			
		||||
        } else if (topic == mm.safetyNetDone) {
 | 
			
		||||
            updateSafetyNetUI((int) result);
 | 
			
		||||
            updateSafetyNetUI((int) topic.getResults()[0]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
    public void onTopicPublished(Topic topic) {
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
        appAdapter.filter(lastFilter);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,9 @@ import com.topjohnwu.magisk.asyncs.ParallelTask;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.CallbackList;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileWriter;
 | 
			
		||||
@@ -113,19 +114,25 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
            mode = (int) params[0];
 | 
			
		||||
            switch (mode) {
 | 
			
		||||
                case 0:
 | 
			
		||||
                    StringBuildingList logList = new StringBuildingList();
 | 
			
		||||
                    Shell.su(logList, "cat " + Const.MAGISK_LOG + " | tail -n 1000");
 | 
			
		||||
                    return logList.getCharSequence();
 | 
			
		||||
                    StringBuilder builder = new StringBuilder();
 | 
			
		||||
                    CallbackList<String> logs = new CallbackList<String>() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void onAddElement(String s) {
 | 
			
		||||
                            builder.append(s).append('\n');
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
                    Shell.Sync.su(logs, "cat " + Const.MAGISK_LOG + " | tail -n 5000");
 | 
			
		||||
                    return builder;
 | 
			
		||||
 | 
			
		||||
                case 1:
 | 
			
		||||
                    Shell.su_raw("echo -n > " + Const.MAGISK_LOG);
 | 
			
		||||
                    Shell.Async.su("echo -n > " + Const.MAGISK_LOG);
 | 
			
		||||
                    SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                    return "";
 | 
			
		||||
 | 
			
		||||
                case 2:
 | 
			
		||||
                    Calendar now = Calendar.getInstance();
 | 
			
		||||
                    String filename = String.format(Locale.US,
 | 
			
		||||
                            "magisk_log_%04d%02d%02d_%02d:%02d:%02d.log",
 | 
			
		||||
                            "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));
 | 
			
		||||
@@ -138,8 +145,16 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    try (FileWriter out = new FileWriter(targetFile)) {
 | 
			
		||||
                        FileWritingList fileWritingList = new FileWritingList(out);
 | 
			
		||||
                        Shell.su(fileWritingList, "cat " + Const.MAGISK_LOG);
 | 
			
		||||
                        CallbackList<String> list = new CallbackList<String>() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void onAddElement(String s) {
 | 
			
		||||
                                try {
 | 
			
		||||
                                    out.write(s);
 | 
			
		||||
                                    out.write("\n");
 | 
			
		||||
                                } catch (IOException ignored) {}
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
                        Shell.Sync.su(list, "cat " + Const.MAGISK_LOG);
 | 
			
		||||
                    } catch (IOException e) {
 | 
			
		||||
                        e.printStackTrace();
 | 
			
		||||
                        return false;
 | 
			
		||||
@@ -187,41 +202,4 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
            exec(2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class StringBuildingList extends Shell.AbstractList<String> {
 | 
			
		||||
 | 
			
		||||
        StringBuilder builder;
 | 
			
		||||
 | 
			
		||||
        StringBuildingList() {
 | 
			
		||||
            builder = new StringBuilder();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean add(String s) {
 | 
			
		||||
            builder.append(s).append("\n");
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CharSequence getCharSequence() {
 | 
			
		||||
            return builder;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class FileWritingList extends Shell.AbstractList<String> {
 | 
			
		||||
 | 
			
		||||
        private FileWriter writer;
 | 
			
		||||
 | 
			
		||||
        FileWritingList(FileWriter out) {
 | 
			
		||||
            writer = out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean add(String s) {
 | 
			
		||||
            try {
 | 
			
		||||
                writer.write(s + "\n");
 | 
			
		||||
            } catch (IOException ignored) {}
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.Application;
 | 
			
		||||
import android.app.job.JobInfo;
 | 
			
		||||
import android.app.job.JobScheduler;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
@@ -8,22 +10,29 @@ import android.content.res.Configuration;
 | 
			
		||||
import android.content.res.Resources;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.preference.PreferenceManager;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.container.Module;
 | 
			
		||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
 | 
			
		||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
 | 
			
		||||
import com.topjohnwu.magisk.services.UpdateCheckService;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.BusyBox;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class MagiskManager extends Application {
 | 
			
		||||
public class MagiskManager extends Shell.ContainerApp {
 | 
			
		||||
 | 
			
		||||
    // Global weak reference to self
 | 
			
		||||
    private static WeakReference<MagiskManager> weakSelf;
 | 
			
		||||
@@ -49,7 +58,6 @@ public class MagiskManager extends Application {
 | 
			
		||||
    public int remoteManagerVersionCode = -1;
 | 
			
		||||
    public String managerLink;
 | 
			
		||||
    public String bootBlock = null;
 | 
			
		||||
    public int snetVersion;
 | 
			
		||||
    public boolean keepVerity = false;
 | 
			
		||||
    public boolean keepEnc = false;
 | 
			
		||||
 | 
			
		||||
@@ -63,8 +71,6 @@ public class MagiskManager extends Application {
 | 
			
		||||
 | 
			
		||||
    public boolean magiskHide;
 | 
			
		||||
    public boolean isDarkTheme;
 | 
			
		||||
    public boolean updateNotification;
 | 
			
		||||
    public boolean suReauth;
 | 
			
		||||
    public int suRequestTimeout;
 | 
			
		||||
    public int suLogTimeout = 14;
 | 
			
		||||
    public int suAccessState;
 | 
			
		||||
@@ -75,13 +81,12 @@ public class MagiskManager extends Application {
 | 
			
		||||
    public String localeConfig;
 | 
			
		||||
    public int updateChannel;
 | 
			
		||||
    public String bootFormat;
 | 
			
		||||
    public String customChannelUrl;
 | 
			
		||||
    public int repoOrder;
 | 
			
		||||
 | 
			
		||||
    // Global resources
 | 
			
		||||
    public SharedPreferences prefs;
 | 
			
		||||
    public SuDatabaseHelper suDB;
 | 
			
		||||
    public RepoDatabaseHelper repoDB;
 | 
			
		||||
    public Shell shell;
 | 
			
		||||
    public Runnable permissionGrantCallback = null;
 | 
			
		||||
 | 
			
		||||
    private static Handler mHandler = new Handler();
 | 
			
		||||
@@ -93,6 +98,26 @@ public class MagiskManager extends Application {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
 | 
			
		||||
        Shell.setFlags(Shell.FLAG_MOUNT_MASTER);
 | 
			
		||||
        Shell.verboseLogging(BuildConfig.DEBUG);
 | 
			
		||||
        BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
 | 
			
		||||
        Shell.setInitializer(new Shell.Initializer() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onRootShellInit(@NonNull Shell shell) {
 | 
			
		||||
                try (InputStream utils = getAssets().open(Const.UTIL_FUNCTIONS);
 | 
			
		||||
                     InputStream sudb = getResources().openRawResource(R.raw.sudb)) {
 | 
			
		||||
                    shell.loadInputStream(null, null, utils);
 | 
			
		||||
                    shell.loadInputStream(null, null, sudb);
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
                shell.run(null, null,
 | 
			
		||||
                        "mount_partitions",
 | 
			
		||||
                        "run_migrations");
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
 | 
			
		||||
 | 
			
		||||
        // Handle duplicate package
 | 
			
		||||
@@ -106,7 +131,15 @@ public class MagiskManager extends Application {
 | 
			
		||||
            } catch (PackageManager.NameNotFoundException ignored) { /* Expected */ }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        suDB = new SuDatabaseHelper(false);
 | 
			
		||||
        suDB = SuDatabaseHelper.getInstance(this);
 | 
			
		||||
 | 
			
		||||
        String pkg = suDB.getStrings(Const.Key.SU_REQUESTER, Const.ORIG_PKG_NAME);
 | 
			
		||||
        if (getPackageName().equals(Const.ORIG_PKG_NAME) && !pkg.equals(Const.ORIG_PKG_NAME)) {
 | 
			
		||||
            suDB.setStrings(Const.Key.SU_REQUESTER, null);
 | 
			
		||||
            Utils.uninstallPkg(pkg);
 | 
			
		||||
            suDB = SuDatabaseHelper.getInstance(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repoDB = new RepoDatabaseHelper(this);
 | 
			
		||||
        defaultLocale = Locale.getDefault();
 | 
			
		||||
        setLocale();
 | 
			
		||||
@@ -135,21 +168,38 @@ public class MagiskManager extends Application {
 | 
			
		||||
        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);
 | 
			
		||||
        suReauth = prefs.getBoolean(Const.Key.SU_REAUTH, false);
 | 
			
		||||
        suAccessState = suDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
 | 
			
		||||
        multiuserMode = suDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
 | 
			
		||||
        suNamespaceMode = suDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
 | 
			
		||||
 | 
			
		||||
        // config
 | 
			
		||||
        isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
 | 
			
		||||
        updateNotification = prefs.getBoolean(Const.Key.UPDATE_NOTIFICATION, true);
 | 
			
		||||
        updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
 | 
			
		||||
        bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
 | 
			
		||||
        snetVersion = prefs.getInt(Const.Key.SNET_VER, -1);
 | 
			
		||||
        customChannelUrl = prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
 | 
			
		||||
        repoOrder = prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_NAME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void toast(String msg, int duration) {
 | 
			
		||||
    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 static void toast(CharSequence msg, int duration) {
 | 
			
		||||
        mHandler.post(() -> Toast.makeText(weakSelf.get(), msg, duration).show());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -158,69 +208,45 @@ public class MagiskManager extends Application {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void loadMagiskInfo() {
 | 
			
		||||
        List<String> ret;
 | 
			
		||||
        ret = Shell.sh("magisk -v");
 | 
			
		||||
        if (!Utils.isValidShellResponse(ret)) {
 | 
			
		||||
            ret = Shell.sh("getprop magisk.version");
 | 
			
		||||
            if (Utils.isValidShellResponse(ret)) {
 | 
			
		||||
                try {
 | 
			
		||||
                    magiskVersionString = ret.get(0);
 | 
			
		||||
                    magiskVersionCode = (int) Double.parseDouble(ret.get(0)) * 10;
 | 
			
		||||
                } catch (NumberFormatException ignored) {}
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            magiskVersionString = ret.get(0).split(":")[0];
 | 
			
		||||
            ret = Shell.sh("magisk -V");
 | 
			
		||||
            try {
 | 
			
		||||
                magiskVersionCode = Integer.parseInt(ret.get(0));
 | 
			
		||||
            } catch (NumberFormatException ignored) {}
 | 
			
		||||
        }
 | 
			
		||||
        if (magiskVersionCode > 1435) {
 | 
			
		||||
            ret = Shell.su("resetprop -p " + Const.MAGISKHIDE_PROP);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = Shell.sh("getprop " + Const.MAGISKHIDE_PROP);
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0;
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            magiskHide = true;
 | 
			
		||||
        }
 | 
			
		||||
            magiskVersionString = Utils.cmd("magisk -v").split(":")[0];
 | 
			
		||||
            magiskVersionCode = Integer.parseInt(Utils.cmd("magisk -V"));
 | 
			
		||||
            String s = Utils.cmd((magiskVersionCode > 1435 ? "resetprop -p " : "getprop ")
 | 
			
		||||
                    + Const.MAGISKHIDE_PROP);
 | 
			
		||||
            magiskHide = s == null || Integer.parseInt(s) != 0;
 | 
			
		||||
        } catch (Exception ignored) {}
 | 
			
		||||
 | 
			
		||||
        ret = Shell.su("echo \"$BOOTIMAGE\"");
 | 
			
		||||
        if (Utils.isValidShellResponse(ret))
 | 
			
		||||
            bootBlock = ret.get(0);
 | 
			
		||||
        bootBlock = Utils.cmd("echo \"$BOOTIMAGE\"");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        ret = Shell.su("echo \"$DTBOIMAGE\"");
 | 
			
		||||
        if (Utils.isValidShellResponse(ret))
 | 
			
		||||
            keepVerity = true;
 | 
			
		||||
    public void getDefaultInstallFlags() {
 | 
			
		||||
        keepVerity = Boolean.parseBoolean(Utils.cmd("getvar KEEPVERITY; echo $KEEPVERITY")) ||
 | 
			
		||||
                Utils.cmd("echo \"$DTBOIMAGE\"") != null;
 | 
			
		||||
 | 
			
		||||
        ret = Shell.su(
 | 
			
		||||
                "getvar KEEPVERITY",
 | 
			
		||||
                "echo $KEEPVERITY");
 | 
			
		||||
        try {
 | 
			
		||||
            if (Utils.isValidShellResponse(ret))
 | 
			
		||||
                keepVerity = Boolean.parseBoolean(ret.get(0));
 | 
			
		||||
        } catch (NumberFormatException ignored) {}
 | 
			
		||||
 | 
			
		||||
        ret = Shell.sh("getprop ro.crypto.state");
 | 
			
		||||
        if (Utils.isValidShellResponse(ret) && ret.get(0).equals("encrypted"))
 | 
			
		||||
            keepEnc = true;
 | 
			
		||||
 | 
			
		||||
        ret = Shell.su(
 | 
			
		||||
                "getvar KEEPFORCEENCRYPT",
 | 
			
		||||
                "echo $KEEPFORCEENCRYPT");
 | 
			
		||||
        try {
 | 
			
		||||
            if (Utils.isValidShellResponse(ret))
 | 
			
		||||
                keepEnc = Boolean.parseBoolean(ret.get(0));
 | 
			
		||||
        } catch (NumberFormatException ignored) {}
 | 
			
		||||
 | 
			
		||||
        if (suDB != null && !SuDatabaseHelper.verified) {
 | 
			
		||||
            suDB.close();
 | 
			
		||||
            suDB = new SuDatabaseHelper();
 | 
			
		||||
        }
 | 
			
		||||
        keepEnc = Boolean.parseBoolean(Utils.cmd("getvar KEEPFORCEENCRYPT; echo $KEEPFORCEENCRYPT")) ||
 | 
			
		||||
                TextUtils.equals("encrypted", Utils.cmd("getprop ro.crypto.state"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setPermissionGrantCallback(Runnable callback) {
 | 
			
		||||
        permissionGrantCallback = callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,9 @@ import android.view.View;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
@@ -99,12 +96,8 @@ public class MainActivity extends Activity
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
            }
 | 
			
		||||
            new MarkDownWindow(this, getString(R.string.app_changelog),
 | 
			
		||||
                    getResources().openRawResource(R.raw.changelog)).exec();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -134,7 +127,7 @@ public class MainActivity extends Activity
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
    public void onTopicPublished(Topic topic) {
 | 
			
		||||
        recreate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -197,22 +190,22 @@ public class MainActivity extends Activity
 | 
			
		||||
        navigationView.setCheckedItem(itemId);
 | 
			
		||||
        switch (itemId) {
 | 
			
		||||
            case R.id.magisk:
 | 
			
		||||
                displayFragment(new MagiskFragment(), "magisk", true);
 | 
			
		||||
                displayFragment(new MagiskFragment(), true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.superuser:
 | 
			
		||||
                displayFragment(new SuperuserFragment(), "superuser", true);
 | 
			
		||||
                displayFragment(new SuperuserFragment(), true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.modules:
 | 
			
		||||
                displayFragment(new ModulesFragment(), "modules", true);
 | 
			
		||||
                displayFragment(new ModulesFragment(), true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.downloads:
 | 
			
		||||
                displayFragment(new ReposFragment(), "downloads", true);
 | 
			
		||||
                displayFragment(new ReposFragment(), true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.magiskhide:
 | 
			
		||||
                displayFragment(new MagiskHideFragment(), Const.Key.MAGISKHIDE, true);
 | 
			
		||||
                displayFragment(new MagiskHideFragment(), true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.log:
 | 
			
		||||
                displayFragment(new LogFragment(), "log", false);
 | 
			
		||||
                displayFragment(new LogFragment(), false);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.settings:
 | 
			
		||||
                startActivity(new Intent(this, SettingsActivity.class));
 | 
			
		||||
@@ -225,12 +218,13 @@ public class MainActivity extends Activity
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void displayFragment(@NonNull Fragment navFragment, String tag, boolean setElevation) {
 | 
			
		||||
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
 | 
			
		||||
    private void displayFragment(@NonNull Fragment navFragment, boolean setElevation) {
 | 
			
		||||
        supportInvalidateOptionsMenu();
 | 
			
		||||
        transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
 | 
			
		||||
        transaction.replace(R.id.content_frame, navFragment, tag).commitNow();
 | 
			
		||||
        if (setElevation) toolbar.setElevation(toolbarElevation);
 | 
			
		||||
        else toolbar.setElevation(0);
 | 
			
		||||
        getSupportFragmentManager()
 | 
			
		||||
                .beginTransaction()
 | 
			
		||||
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
 | 
			
		||||
                .replace(R.id.content_frame, navFragment)
 | 
			
		||||
                .commitNow();
 | 
			
		||||
        toolbar.setElevation(setElevation ? toolbarElevation : 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,9 @@ import com.topjohnwu.magisk.asyncs.LoadModules;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.container.Module;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -79,7 +79,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
    public void onTopicPublished(Topic topic) {
 | 
			
		||||
        updateUI();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -113,16 +113,16 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        switch (item.getItemId()) {
 | 
			
		||||
            case R.id.reboot:
 | 
			
		||||
                Shell.su_raw("/system/bin/reboot");
 | 
			
		||||
                Shell.Async.su("/system/bin/reboot");
 | 
			
		||||
                return true;
 | 
			
		||||
            case R.id.reboot_recovery:
 | 
			
		||||
                Shell.su_raw("/system/bin/reboot recovery");
 | 
			
		||||
                Shell.Async.su("/system/bin/reboot recovery");
 | 
			
		||||
                return true;
 | 
			
		||||
            case R.id.reboot_bootloader:
 | 
			
		||||
                Shell.su_raw("/system/bin/reboot bootloader");
 | 
			
		||||
                Shell.Async.su("/system/bin/reboot bootloader");
 | 
			
		||||
                return true;
 | 
			
		||||
            case R.id.reboot_download:
 | 
			
		||||
                Shell.su_raw("/system/bin/reboot download");
 | 
			
		||||
                Shell.Async.su("/system/bin/reboot download");
 | 
			
		||||
                return true;
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.AlertDialog;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.v4.widget.SwipeRefreshLayout;
 | 
			
		||||
@@ -7,6 +8,7 @@ import android.support.v7.widget.RecyclerView;
 | 
			
		||||
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.SearchView;
 | 
			
		||||
@@ -15,6 +17,7 @@ import android.widget.TextView;
 | 
			
		||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
@@ -24,6 +27,7 @@ import butterknife.Unbinder;
 | 
			
		||||
public class ReposFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
 | 
			
		||||
    private Unbinder unbinder;
 | 
			
		||||
    private MagiskManager mm;
 | 
			
		||||
    @BindView(R.id.recyclerView) RecyclerView recyclerView;
 | 
			
		||||
    @BindView(R.id.empty_rv) TextView emptyRv;
 | 
			
		||||
    @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
 | 
			
		||||
@@ -41,8 +45,9 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 | 
			
		||||
        View view = inflater.inflate(R.layout.fragment_repos, container, false);
 | 
			
		||||
        unbinder = ButterKnife.bind(this, view);
 | 
			
		||||
        mm = getApplication();
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(mm.repoLoadDone.isPending());
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(() -> {
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
@@ -57,7 +62,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onResume() {
 | 
			
		||||
        adapter = new ReposAdapter(getApplication().repoDB, getApplication().moduleMap);
 | 
			
		||||
        adapter = new ReposAdapter(mm.repoDB, mm.moduleMap);
 | 
			
		||||
        recyclerView.setAdapter(adapter);
 | 
			
		||||
        super.onResume();
 | 
			
		||||
    }
 | 
			
		||||
@@ -69,7 +74,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
    public void onTopicPublished(Topic topic) {
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
        recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
 | 
			
		||||
        emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
 | 
			
		||||
@@ -77,7 +82,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Topic[] getSubscription() {
 | 
			
		||||
        return new Topic[] { getApplication().repoLoadDone };
 | 
			
		||||
        return new Topic[] { mm.repoLoadDone };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -98,6 +103,21 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == R.id.repo_sort) {
 | 
			
		||||
            new AlertDialog.Builder(getActivity())
 | 
			
		||||
                .setTitle(R.string.sorting_order)
 | 
			
		||||
                .setSingleChoiceItems(R.array.sorting_orders, mm.repoOrder, (d, which) -> {
 | 
			
		||||
                    mm.repoOrder = which;
 | 
			
		||||
                    mm.prefs.edit().putInt(Const.Key.REPO_ORDER, mm.repoOrder).apply();
 | 
			
		||||
                    adapter.notifyDBChanged();
 | 
			
		||||
                    d.dismiss();
 | 
			
		||||
                }).show();
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroyView() {
 | 
			
		||||
        super.onDestroyView();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.Manifest;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
@@ -21,11 +22,14 @@ import android.widget.Toast;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.HideManager;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
@@ -65,7 +69,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
    public void onTopicPublished(Topic topic) {
 | 
			
		||||
        recreate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -97,6 +101,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
            PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
 | 
			
		||||
            PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
 | 
			
		||||
            Preference hideManager = findPreference("hide");
 | 
			
		||||
            Preference restoreManager = findPreference("restore");
 | 
			
		||||
            findPreference("clear").setOnPreferenceClickListener((pref) -> {
 | 
			
		||||
                prefs.edit().remove(Const.Key.ETAG_KEY).apply();
 | 
			
		||||
                mm.repoDB.clearRepo();
 | 
			
		||||
@@ -112,13 +117,14 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
            multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
 | 
			
		||||
            namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
 | 
			
		||||
            SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
 | 
			
		||||
            SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
 | 
			
		||||
 | 
			
		||||
            updateChannel.setOnPreferenceChangeListener((pref, o) -> {
 | 
			
		||||
                mm.updateChannel = Integer.parseInt((String) o);
 | 
			
		||||
                if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) {
 | 
			
		||||
                    View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null);
 | 
			
		||||
                    EditText url = v.findViewById(R.id.custom_url);
 | 
			
		||||
                    url.setText(mm.customChannelUrl);
 | 
			
		||||
                    url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
 | 
			
		||||
                    new AlertDialog.Builder(getActivity())
 | 
			
		||||
                        .setTitle(R.string.settings_update_custom)
 | 
			
		||||
                        .setView(v)
 | 
			
		||||
@@ -144,14 +150,39 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
                reauth.setSummary(R.string.android_o_not_support);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
                });
 | 
			
		||||
            // Disable fingerprint option if not possible
 | 
			
		||||
            if (!FingerprintHelper.canUseFingerprint()) {
 | 
			
		||||
                fingerprint.setEnabled(false);
 | 
			
		||||
                fingerprint.setSummary(R.string.disable_fingerprint);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (mm.magiskVersionCode >= 1440) {
 | 
			
		||||
                if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
 | 
			
		||||
                    hideManager.setOnPreferenceClickListener((pref) -> {
 | 
			
		||||
                        new HideManager(getActivity()).exec();
 | 
			
		||||
                        return true;
 | 
			
		||||
                    });
 | 
			
		||||
                    generalCatagory.removePreference(restoreManager);
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (Utils.checkNetworkStatus()) {
 | 
			
		||||
                        restoreManager.setOnPreferenceClickListener((pref) -> {
 | 
			
		||||
                            Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
 | 
			
		||||
                                Intent intent = new Intent(mm, ManagerUpdate.class);
 | 
			
		||||
                                intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
 | 
			
		||||
                                intent.putExtra(Const.Key.INTENT_SET_FILENAME,
 | 
			
		||||
                                        Utils.fmt("MagiskManager-v%s(%d).apk",
 | 
			
		||||
                                        mm.remoteManagerVersionString, mm.remoteManagerVersionCode));
 | 
			
		||||
                                mm.sendBroadcast(intent);
 | 
			
		||||
                            });
 | 
			
		||||
                            return true;
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        generalCatagory.removePreference(restoreManager);
 | 
			
		||||
                    }
 | 
			
		||||
                    generalCatagory.removePreference(hideManager);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                generalCatagory.removePreference(restoreManager);
 | 
			
		||||
                generalCatagory.removePreference(hideManager);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -216,26 +247,28 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
                    break;
 | 
			
		||||
                case Const.Key.COREONLY:
 | 
			
		||||
                    if (prefs.getBoolean(key, false)) {
 | 
			
		||||
                        Utils.createFile(Const.MAGISK_DISABLE_FILE);
 | 
			
		||||
                        try {
 | 
			
		||||
                            Const.MAGISK_DISABLE_FILE.createNewFile();
 | 
			
		||||
                        } catch (IOException ignored) {}
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Utils.removeItem(Const.MAGISK_DISABLE_FILE);
 | 
			
		||||
                        Const.MAGISK_DISABLE_FILE.delete();
 | 
			
		||||
                    }
 | 
			
		||||
                    Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
 | 
			
		||||
                    break;
 | 
			
		||||
                case Const.Key.MAGISKHIDE:
 | 
			
		||||
                    if (prefs.getBoolean(key, false)) {
 | 
			
		||||
                        Shell.su_raw("magiskhide --enable");
 | 
			
		||||
                        Shell.Async.su("magiskhide --enable");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Shell.su_raw("magiskhide --disable");
 | 
			
		||||
                        Shell.Async.su("magiskhide --disable");
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case Const.Key.HOSTS:
 | 
			
		||||
                    if (prefs.getBoolean(key, false)) {
 | 
			
		||||
                        Shell.su_raw(
 | 
			
		||||
                        Shell.Async.su(
 | 
			
		||||
                                "cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE(),
 | 
			
		||||
                                "mount -o bind " + Const.MAGISK_HOST_FILE() + " /system/etc/hosts");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Shell.su_raw(
 | 
			
		||||
                        Shell.Async.su(
 | 
			
		||||
                                "umount -l /system/etc/hosts",
 | 
			
		||||
                                "rm -f " + Const.MAGISK_HOST_FILE());
 | 
			
		||||
                    }
 | 
			
		||||
@@ -252,6 +285,9 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
                case Const.Key.UPDATE_CHANNEL:
 | 
			
		||||
                    new CheckUpdates().exec();
 | 
			
		||||
                    break;
 | 
			
		||||
                case Const.Key.CHECK_UPDATES:
 | 
			
		||||
                    mm.setupUpdateCheck();
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            mm.loadConfig();
 | 
			
		||||
            setSummary();
 | 
			
		||||
@@ -275,7 +311,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onTopicPublished(Topic topic, Object result) {
 | 
			
		||||
        public void onTopicPublished(Topic topic) {
 | 
			
		||||
            setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,6 @@ package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.NotificationChannel;
 | 
			
		||||
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.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
@@ -15,18 +11,12 @@ import com.topjohnwu.magisk.asyncs.LoadModules;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.services.UpdateCheckService;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
public class SplashActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getDarkTheme() {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
@@ -34,6 +24,7 @@ public class SplashActivity extends Activity {
 | 
			
		||||
        MagiskManager mm = getMagiskManager();
 | 
			
		||||
 | 
			
		||||
        mm.loadMagiskInfo();
 | 
			
		||||
        mm.getDefaultInstallFlags();
 | 
			
		||||
        Utils.loadPrefs();
 | 
			
		||||
 | 
			
		||||
        // Dynamic detect all locales
 | 
			
		||||
@@ -49,54 +40,24 @@ public class SplashActivity extends Activity {
 | 
			
		||||
        LoadModules loadModuleTask = new LoadModules();
 | 
			
		||||
 | 
			
		||||
        if (Utils.checkNetworkStatus()) {
 | 
			
		||||
 | 
			
		||||
            // Fire update check
 | 
			
		||||
            new CheckUpdates().exec();
 | 
			
		||||
 | 
			
		||||
            // Add repo update check
 | 
			
		||||
            loadModuleTask.setCallBack(() -> new UpdateRepos(false).exec());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Magisk working as expected
 | 
			
		||||
        if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
 | 
			
		||||
 | 
			
		||||
            // Add update checking service
 | 
			
		||||
            if (Const.Value.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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update check service
 | 
			
		||||
            mm.setupUpdateCheck();
 | 
			
		||||
            // Fire asynctasks
 | 
			
		||||
            loadModuleTask.exec();
 | 
			
		||||
 | 
			
		||||
            // Check dtbo status
 | 
			
		||||
            Utils.patchDTBO();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Write back default values
 | 
			
		||||
        mm.prefs.edit()
 | 
			
		||||
                .putBoolean(Const.Key.DARK_THEME, mm.isDarkTheme)
 | 
			
		||||
                .putBoolean(Const.Key.MAGISKHIDE, mm.magiskHide)
 | 
			
		||||
                .putBoolean(Const.Key.UPDATE_NOTIFICATION, mm.updateNotification)
 | 
			
		||||
                .putBoolean(Const.Key.HOSTS, Utils.itemExist(Const.MAGISK_HOST_FILE()))
 | 
			
		||||
                .putBoolean(Const.Key.COREONLY, Utils.itemExist(Const.MAGISK_DISABLE_FILE))
 | 
			
		||||
                .putBoolean(Const.Key.SU_REAUTH, mm.suReauth)
 | 
			
		||||
                .putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(mm.suRequestTimeout))
 | 
			
		||||
                .putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(mm.suResponseType))
 | 
			
		||||
                .putString(Const.Key.SU_NOTIFICATION, String.valueOf(mm.suNotificationType))
 | 
			
		||||
                .putString(Const.Key.ROOT_ACCESS, String.valueOf(mm.suAccessState))
 | 
			
		||||
                .putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(mm.multiuserMode))
 | 
			
		||||
                .putString(Const.Key.SU_MNT_NS, String.valueOf(mm.suNamespaceMode))
 | 
			
		||||
                .putString(Const.Key.UPDATE_CHANNEL, String.valueOf(mm.updateChannel))
 | 
			
		||||
                .putString(Const.Key.LOCALE, mm.localeConfig)
 | 
			
		||||
                .putString(Const.Key.BOOT_FORMAT, mm.bootFormat)
 | 
			
		||||
                .putInt(Const.Key.UPDATE_SERVICE_VER, Const.Value.UPDATE_SERVICE_VER)
 | 
			
		||||
                .apply();
 | 
			
		||||
        mm.writeConfig();
 | 
			
		||||
 | 
			
		||||
        mm.hasInit = true;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,9 @@ import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Topic;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
@@ -65,30 +65,17 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
 | 
			
		||||
        holder.appName.setText(info.loadLabel(pm));
 | 
			
		||||
        holder.appPackage.setText(info.packageName);
 | 
			
		||||
 | 
			
		||||
        // Remove all listeners
 | 
			
		||||
        holder.itemView.setOnClickListener(null);
 | 
			
		||||
        holder.checkBox.setOnCheckedChangeListener(null);
 | 
			
		||||
 | 
			
		||||
        if (Const.SN_DEFAULTLIST.contains(info.packageName)) {
 | 
			
		||||
            holder.checkBox.setChecked(true);
 | 
			
		||||
            holder.checkBox.setEnabled(false);
 | 
			
		||||
            holder.itemView.setOnClickListener(v ->
 | 
			
		||||
                SnackbarMaker.make(holder.itemView,
 | 
			
		||||
                        R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).show()
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            holder.checkBox.setEnabled(true);
 | 
			
		||||
            holder.checkBox.setChecked(mHideList.contains(info.packageName));
 | 
			
		||||
            holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
 | 
			
		||||
                if (isChecked) {
 | 
			
		||||
                    Shell.su_raw("magiskhide --add " + info.packageName);
 | 
			
		||||
                    mHideList.add(info.packageName);
 | 
			
		||||
                } else {
 | 
			
		||||
                    Shell.su_raw("magiskhide --rm " + info.packageName);
 | 
			
		||||
                    mHideList.remove(info.packageName);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
@@ -108,7 +95,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.app_icon) ImageView appIcon;
 | 
			
		||||
        @BindView(R.id.app_name) TextView appName;
 | 
			
		||||
        @BindView(R.id.app_package) TextView appPackage;
 | 
			
		||||
        @BindView(R.id.package_name) TextView appPackage;
 | 
			
		||||
        @BindView(R.id.checkbox) CheckBox checkBox;
 | 
			
		||||
 | 
			
		||||
        ViewHolder(View itemView) {
 | 
			
		||||
@@ -155,7 +142,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
 | 
			
		||||
            }
 | 
			
		||||
            Collections.sort(mOriginalList, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
 | 
			
		||||
                    .compareTo(b.loadLabel(pm).toString().toLowerCase()));
 | 
			
		||||
            mHideList = Shell.su("magiskhide --ls");
 | 
			
		||||
            mHideList = Shell.Sync.su("magiskhide --ls");
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ import android.widget.TextView;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.container.Module;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -95,6 +95,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
 | 
			
		||||
        String author = repo.getAuthor();
 | 
			
		||||
        holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
 | 
			
		||||
        holder.description.setText(repo.getDescription());
 | 
			
		||||
        holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
 | 
			
		||||
 | 
			
		||||
        holder.infoLayout.setOnClickListener(v ->
 | 
			
		||||
                new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec());
 | 
			
		||||
@@ -180,6 +181,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
 | 
			
		||||
        @BindView(R.id.author) TextView author;
 | 
			
		||||
        @BindView(R.id.info_layout) LinearLayout infoLayout;
 | 
			
		||||
        @BindView(R.id.download) ImageView downloadImage;
 | 
			
		||||
        @BindView(R.id.update_time) TextView updateTime;
 | 
			
		||||
 | 
			
		||||
        RepoHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,14 +4,15 @@ import android.app.Activity;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
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.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.lang.reflect.Proxy;
 | 
			
		||||
@@ -21,42 +22,57 @@ import dalvik.system.DexClassLoader;
 | 
			
		||||
 | 
			
		||||
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
 | 
			
		||||
 | 
			
		||||
    private File dexPath;
 | 
			
		||||
    public static final File dexPath =
 | 
			
		||||
            new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
 | 
			
		||||
    private DexClassLoader loader;
 | 
			
		||||
    private Class<?> helperClazz, callbackClazz;
 | 
			
		||||
 | 
			
		||||
    public CheckSafetyNet(Activity activity) {
 | 
			
		||||
        super(activity);
 | 
			
		||||
        dexPath = new File(activity.getCacheDir().getParent() + "/snet", "snet.apk");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPreExecute() {
 | 
			
		||||
        MagiskManager mm = MagiskManager.get();
 | 
			
		||||
        if (mm.snetVersion != Const.Value.SNET_VER) {
 | 
			
		||||
            Shell.sh("rm -rf " + dexPath.getParent());
 | 
			
		||||
    private void dlSnet() throws IOException {
 | 
			
		||||
        Shell.Sync.sh("rm -rf " + dexPath.getParent());
 | 
			
		||||
        HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
 | 
			
		||||
        dexPath.getParentFile().mkdir();
 | 
			
		||||
        try (
 | 
			
		||||
                OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
 | 
			
		||||
                InputStream in = new BufferedInputStream(conn.getInputStream())) {
 | 
			
		||||
            ShellUtils.pump(in, out);
 | 
			
		||||
        }
 | 
			
		||||
        mm.snetVersion = Const.Value.SNET_VER;
 | 
			
		||||
        mm.prefs.edit().putInt(Const.Key.SNET_VER, Const.Value.SNET_VER).apply();
 | 
			
		||||
        conn.disconnect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void loadClasses() throws ClassNotFoundException {
 | 
			
		||||
        loader = new DexClassLoader(dexPath.toString(), dexPath.getParent(),
 | 
			
		||||
                null, ClassLoader.getSystemClassLoader());
 | 
			
		||||
        helperClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetHelper");
 | 
			
		||||
        callbackClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetCallback");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Exception doInBackground(Void... voids) {
 | 
			
		||||
        int snet_ver = -1;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (!dexPath.exists()) {
 | 
			
		||||
                HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
 | 
			
		||||
                dexPath.getParentFile().mkdir();
 | 
			
		||||
                try (
 | 
			
		||||
                        OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
 | 
			
		||||
                        InputStream in = new BufferedInputStream(conn.getInputStream())) {
 | 
			
		||||
                    Utils.inToOut(in, out);
 | 
			
		||||
                }
 | 
			
		||||
                conn.disconnect();
 | 
			
		||||
            if (!dexPath.exists())
 | 
			
		||||
                dlSnet();
 | 
			
		||||
            loadClasses();
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                snet_ver = (int) helperClazz.getMethod("getVersion").invoke(null);
 | 
			
		||||
            } catch (NoSuchMethodException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (snet_ver != Const.SNET_VER) {
 | 
			
		||||
                dlSnet();
 | 
			
		||||
                loadClasses();
 | 
			
		||||
            }
 | 
			
		||||
            loader = new DexClassLoader(dexPath.toString(), dexPath.getParent(),
 | 
			
		||||
                    null, ClassLoader.getSystemClassLoader());
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return e;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -65,8 +81,6 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
 | 
			
		||||
        MagiskManager mm = MagiskManager.get();
 | 
			
		||||
        try {
 | 
			
		||||
            if (err != null) throw err;
 | 
			
		||||
            Class<?> helperClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetHelper");
 | 
			
		||||
            Class<?> callbackClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetCallback");
 | 
			
		||||
            Object helper = helperClazz.getConstructors()[0].newInstance(
 | 
			
		||||
                    getActivity(), dexPath.getPath(), Proxy.newProxyInstance(
 | 
			
		||||
                            loader, new Class[] { callbackClazz }, (proxy, method, args) -> {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
                jsonStr = WebService.getString(Const.Url.BETA_URL);
 | 
			
		||||
                break;
 | 
			
		||||
            case Const.Value.CUSTOM_CHANNEL:
 | 
			
		||||
                jsonStr = WebService.getString(mm.customChannelUrl);
 | 
			
		||||
                jsonStr = WebService.getString(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
@@ -54,7 +54,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        MagiskManager mm = MagiskManager.get();
 | 
			
		||||
        if (showNotification && mm.updateNotification) {
 | 
			
		||||
        if (showNotification) {
 | 
			
		||||
            if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
 | 
			
		||||
                ShowUI.managerUpdateNotification();
 | 
			
		||||
            } else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,10 @@ import android.view.View;
 | 
			
		||||
import com.topjohnwu.magisk.FlashActivity;
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ZipUtils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
import com.topjohnwu.superuser.ShellUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedInputStream;
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
@@ -38,8 +39,8 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
 | 
			
		||||
 | 
			
		||||
    private boolean unzipAndCheck() throws Exception {
 | 
			
		||||
        ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
 | 
			
		||||
        List<String> ret = Utils.readFile(new File(mCachedFile.getParentFile(), "updater-script"));
 | 
			
		||||
        return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
 | 
			
		||||
        String s = Utils.cmd("head -n 1 " + new File(mCachedFile.getParentFile(), "updater-script"));
 | 
			
		||||
        return s != null && s.contains("#MAGISK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -55,7 +56,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
 | 
			
		||||
            ) {
 | 
			
		||||
                if (in == null) throw new FileNotFoundException();
 | 
			
		||||
                InputStream buf= new BufferedInputStream(in);
 | 
			
		||||
                Utils.inToOut(buf, out);
 | 
			
		||||
                ShellUtils.pump(buf, out);
 | 
			
		||||
            } catch (FileNotFoundException e) {
 | 
			
		||||
                console.add("! Invalid Uri");
 | 
			
		||||
                throw e;
 | 
			
		||||
@@ -65,7 +66,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
 | 
			
		||||
            }
 | 
			
		||||
            if (!unzipAndCheck()) return 0;
 | 
			
		||||
            console.add("- Installing " + Utils.getNameFromUri(mm, mUri));
 | 
			
		||||
            Shell.getShell().run(console, logs,
 | 
			
		||||
            Shell.Sync.su(console, logs,
 | 
			
		||||
                    "cd " + mCachedFile.getParent(),
 | 
			
		||||
                    "BOOTMODE=true sh update-binary dummy 1 " + mCachedFile + " || echo 'Failed!'"
 | 
			
		||||
            );
 | 
			
		||||
@@ -85,7 +86,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Integer result) {
 | 
			
		||||
        FlashActivity activity = (FlashActivity) getActivity();
 | 
			
		||||
        Shell.su_raw(
 | 
			
		||||
        Shell.Async.su(
 | 
			
		||||
                "rm -rf " + mCachedFile.getParent(),
 | 
			
		||||
                "rm -rf " + Const.TMP_FOLDER_PATH
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -4,18 +4,19 @@ import android.app.Activity;
 | 
			
		||||
import android.app.ProgressDialog;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.crypto.JarMap;
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
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.SuFile;
 | 
			
		||||
import com.topjohnwu.superuser.io.SuFileOutputStream;
 | 
			
		||||
import com.topjohnwu.utils.JarMap;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.security.SecureRandom;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.jar.JarEntry;
 | 
			
		||||
 | 
			
		||||
public class HideManager extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
@@ -104,8 +105,7 @@ public class HideManager extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
        MagiskManager mm = MagiskManager.get();
 | 
			
		||||
 | 
			
		||||
        // Generate a new unhide app with random package name
 | 
			
		||||
        File repack = new File(Const.EXTERNAL_PATH, "repack.apk");
 | 
			
		||||
        repack.getParentFile().mkdirs();
 | 
			
		||||
        SuFile repack = new SuFile("/data/local/tmp/repack.apk", true);
 | 
			
		||||
        String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@@ -123,19 +123,18 @@ public class HideManager extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
            apk.getOutputStream(je).write(xml);
 | 
			
		||||
 | 
			
		||||
            // Sign the APK
 | 
			
		||||
            ZipUtils.signZip(apk, repack, false);
 | 
			
		||||
            ZipUtils.signZip(apk, new SuFileOutputStream(repack));
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Install the application
 | 
			
		||||
 | 
			
		||||
        List<String> ret = Shell.su(Utils.fmt("pm install %s >/dev/null && echo true || echo false", repack));
 | 
			
		||||
        repack.delete();
 | 
			
		||||
        if (!Utils.isValidShellResponse(ret) || !Boolean.parseBoolean(ret.get(0)))
 | 
			
		||||
        if (!ShellUtils.fastCmdResult(Shell.getShell(), "pm install " + repack))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        repack.delete();
 | 
			
		||||
 | 
			
		||||
        mm.suDB.setStrings(Const.Key.SU_REQUESTER, pkg);
 | 
			
		||||
        Utils.dumpPrefs();
 | 
			
		||||
        Utils.uninstallPkg(Const.ORIG_PKG_NAME);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,22 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.res.AssetManager;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.crypto.SignBoot;
 | 
			
		||||
import com.topjohnwu.magisk.FlashActivity;
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.container.TarEntry;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
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.SuFile;
 | 
			
		||||
import com.topjohnwu.superuser.io.SuFileInputStream;
 | 
			
		||||
import com.topjohnwu.utils.SignBoot;
 | 
			
		||||
 | 
			
		||||
import org.kamranzafar.jtar.TarInputStream;
 | 
			
		||||
import org.kamranzafar.jtar.TarOutputStream;
 | 
			
		||||
@@ -28,7 +30,6 @@ import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +42,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
    private List<String> console, logs;
 | 
			
		||||
    private String mBootLocation;
 | 
			
		||||
    private int mode;
 | 
			
		||||
    private File install;
 | 
			
		||||
 | 
			
		||||
    private InstallMagisk(Activity context, List<String> console, List<String> logs, Uri zip) {
 | 
			
		||||
        super(context);
 | 
			
		||||
@@ -65,12 +67,12 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
    protected Boolean doInBackground(Void... voids) {
 | 
			
		||||
        MagiskManager mm = MagiskManager.get();
 | 
			
		||||
 | 
			
		||||
        File install = new File(
 | 
			
		||||
        install = new File(
 | 
			
		||||
                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
 | 
			
		||||
                        mm.createDeviceProtectedStorageContext() :
 | 
			
		||||
                        mm).getFilesDir().getParent()
 | 
			
		||||
                        mm.createDeviceProtectedStorageContext() : mm)
 | 
			
		||||
                        .getFilesDir().getParent()
 | 
			
		||||
                , "install");
 | 
			
		||||
        Shell.sh_raw("rm -rf " + install);
 | 
			
		||||
        Shell.Sync.sh("rm -rf " + install);
 | 
			
		||||
 | 
			
		||||
        List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
 | 
			
		||||
        String arch;
 | 
			
		||||
@@ -102,17 +104,15 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
                console.add("! Cannot unzip zip");
 | 
			
		||||
                throw e;
 | 
			
		||||
            }
 | 
			
		||||
            Shell.sh("chmod 755 " + install + "/*");
 | 
			
		||||
            Shell.Sync.sh("chmod 755 " + install + "/*");
 | 
			
		||||
 | 
			
		||||
            File boot = new File(install, "boot.img");
 | 
			
		||||
            boolean highCompression = false;
 | 
			
		||||
            switch (mode) {
 | 
			
		||||
                case PATCH_MODE:
 | 
			
		||||
                    console.add("- Use boot image: " + boot);
 | 
			
		||||
                    // Copy boot image to local
 | 
			
		||||
                    try (
 | 
			
		||||
                        InputStream in = mm.getContentResolver().openInputStream(mBootImg);
 | 
			
		||||
                        OutputStream out = new FileOutputStream(boot)
 | 
			
		||||
                    try (InputStream in = mm.getContentResolver().openInputStream(mBootImg);
 | 
			
		||||
                         OutputStream out = new FileOutputStream(boot)
 | 
			
		||||
                    ) {
 | 
			
		||||
                        InputStream source;
 | 
			
		||||
                        if (in == null) throw new FileNotFoundException();
 | 
			
		||||
@@ -130,7 +130,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
                            // Direct copy raw image
 | 
			
		||||
                            source = new BufferedInputStream(in);
 | 
			
		||||
                        }
 | 
			
		||||
                        Utils.inToOut(source, out);
 | 
			
		||||
                        ShellUtils.pump(source, out);
 | 
			
		||||
                    } catch (FileNotFoundException e) {
 | 
			
		||||
                        console.add("! Invalid Uri");
 | 
			
		||||
                        throw e;
 | 
			
		||||
@@ -140,21 +140,16 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case DIRECT_MODE:
 | 
			
		||||
                    console.add("- Use boot image: " + mBootLocation);
 | 
			
		||||
                    console.add("- Patch boot/ramdisk image: " + mBootLocation);
 | 
			
		||||
                    if (mm.remoteMagiskVersionCode >= 1463) {
 | 
			
		||||
                        List<String> ret = new ArrayList<>();
 | 
			
		||||
                         Shell.getShell().run(ret, logs,
 | 
			
		||||
                                install + "/magiskboot --parse " + mBootLocation,
 | 
			
		||||
                                "echo $?"
 | 
			
		||||
                        );
 | 
			
		||||
                        if (Utils.isValidShellResponse(ret)) {
 | 
			
		||||
                            highCompression = Integer.parseInt(ret.get(ret.size() - 1)) == 2;
 | 
			
		||||
                            if (highCompression)
 | 
			
		||||
                                console.add("! Insufficient boot partition size detected");
 | 
			
		||||
                        }
 | 
			
		||||
                        highCompression = Integer.parseInt(Utils.cmd(Utils.fmt(
 | 
			
		||||
                                "%s/magiskboot --parse %s; echo $?",
 | 
			
		||||
                                install, mBootLocation))) == 2;
 | 
			
		||||
                        if (highCompression)
 | 
			
		||||
                            console.add("! Insufficient boot partition size detected");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (boot.createNewFile()) {
 | 
			
		||||
                        Shell.su("cat " + mBootLocation + " > " + boot);
 | 
			
		||||
                        Shell.Sync.su("cat " + mBootLocation + " > " + boot);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        console.add("! Dump boot image failed");
 | 
			
		||||
                        return false;
 | 
			
		||||
@@ -168,22 +163,15 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
            try (InputStream in = new FileInputStream(boot)) {
 | 
			
		||||
                isSigned = SignBoot.verifySignature(in, null);
 | 
			
		||||
                if (isSigned) {
 | 
			
		||||
                    console.add("- Signed boot image detected");
 | 
			
		||||
                    console.add("- Boot image is signed with AVB 1.0");
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                console.add("! Unable to check signature");
 | 
			
		||||
                throw e;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Force non-root shell
 | 
			
		||||
            Shell shell;
 | 
			
		||||
            if (Shell.rootAccess())
 | 
			
		||||
                shell = new Shell("sh");
 | 
			
		||||
            else
 | 
			
		||||
                shell = Shell.getShell();
 | 
			
		||||
 | 
			
		||||
            // Patch boot image
 | 
			
		||||
            shell.run(console, logs,
 | 
			
		||||
            Shell.Sync.sh(console, logs,
 | 
			
		||||
                    "cd " + install,
 | 
			
		||||
                    Utils.fmt("KEEPFORCEENCRYPT=%b KEEPVERITY=%b HIGHCOMP=%b " +
 | 
			
		||||
                                    "sh update-binary indep boot_patch.sh %s || echo 'Failed!'",
 | 
			
		||||
@@ -192,27 +180,22 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
            if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            shell.run(null, null,
 | 
			
		||||
                    "mv -f new-boot.img ../",
 | 
			
		||||
            Shell.Sync.sh("mv -f new-boot.img ../",
 | 
			
		||||
                    "mv bin/busybox busybox",
 | 
			
		||||
                    "rm -rf bin *.img update-binary",
 | 
			
		||||
                    "cd /");
 | 
			
		||||
 | 
			
		||||
            File patched_boot = new File(install.getParent(), "new-boot.img");
 | 
			
		||||
            SuFile patched_boot = new SuFile(install.getParent(), "new-boot.img");
 | 
			
		||||
 | 
			
		||||
            if (isSigned) {
 | 
			
		||||
                console.add("- Signing boot image");
 | 
			
		||||
                console.add("- Signing boot image with test keys");
 | 
			
		||||
                File signed = new File(install.getParent(), "signed.img");
 | 
			
		||||
                AssetManager assets = mm.getAssets();
 | 
			
		||||
                try (
 | 
			
		||||
                        InputStream in = new FileInputStream(patched_boot);
 | 
			
		||||
                        OutputStream out = new BufferedOutputStream(new FileOutputStream(signed));
 | 
			
		||||
                        InputStream keyIn = assets.open(Const.PRIVATE_KEY_NAME);
 | 
			
		||||
                        InputStream certIn = assets.open(Const.PUBLIC_KEY_NAME)
 | 
			
		||||
                try (InputStream in = new SuFileInputStream(patched_boot);
 | 
			
		||||
                     OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))
 | 
			
		||||
                ) {
 | 
			
		||||
                    SignBoot.doSignature("/boot", in, out, keyIn, certIn);
 | 
			
		||||
                    SignBoot.doSignature("/boot", in, out, null, null);
 | 
			
		||||
                }
 | 
			
		||||
                shell.run_raw(false, false, "mv -f " + signed + " " + patched_boot);
 | 
			
		||||
                Shell.Sync.sh("mv -f " + signed + " " + patched_boot);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (mode) {
 | 
			
		||||
@@ -230,8 +213,8 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
                            out = new BufferedOutputStream(new FileOutputStream(dest));
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    try (InputStream in = new BufferedInputStream(new FileInputStream(patched_boot))) {
 | 
			
		||||
                        Utils.inToOut(in, out);
 | 
			
		||||
                    try (InputStream in = new SuFileInputStream(patched_boot)) {
 | 
			
		||||
                        ShellUtils.pump(in, out);
 | 
			
		||||
                        out.close();
 | 
			
		||||
                    }
 | 
			
		||||
                    console.add("");
 | 
			
		||||
@@ -242,7 +225,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
                    break;
 | 
			
		||||
                case DIRECT_MODE:
 | 
			
		||||
                    String binPath = mm.remoteMagiskVersionCode >= 1464 ? "/data/adb/magisk" : "/data/magisk";
 | 
			
		||||
                    Shell.getShell().run(console, logs,
 | 
			
		||||
                    Shell.Sync.su(console, logs,
 | 
			
		||||
                            Utils.fmt("rm -rf %s/*; mkdir -p %s; chmod 700 /data/adb", binPath, binPath),
 | 
			
		||||
                            Utils.fmt("cp -af %s/* %s; rm -rf %s", install, binPath, install),
 | 
			
		||||
                            Utils.fmt("flash_boot_image %s %s", patched_boot, mBootLocation),
 | 
			
		||||
@@ -267,6 +250,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
    protected void onPostExecute(Boolean result) {
 | 
			
		||||
        FlashActivity activity = (FlashActivity) getActivity();
 | 
			
		||||
        if (!result) {
 | 
			
		||||
            Shell.Async.sh("rm -rf " + install);
 | 
			
		||||
            console.add("! Installation failed");
 | 
			
		||||
            activity.reboot.setVisibility(View.GONE);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.container.Module;
 | 
			
		||||
import com.topjohnwu.magisk.container.ValueSortedMap;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -12,7 +12,7 @@ public class LoadModules extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    private List<String> getModList() {
 | 
			
		||||
        String command = "ls -d " + Const.MAGISK_PATH() + "/* | grep -v lost+found";
 | 
			
		||||
        return Shell.su(command);
 | 
			
		||||
        return Shell.Sync.su(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,8 @@ import android.webkit.WebView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.WebService;
 | 
			
		||||
import com.topjohnwu.superuser.ShellUtils;
 | 
			
		||||
 | 
			
		||||
import org.commonmark.node.Node;
 | 
			
		||||
import org.commonmark.parser.Parser;
 | 
			
		||||
@@ -44,7 +44,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
 | 
			
		||||
            md = WebService.getString(mUrl);
 | 
			
		||||
        } else {
 | 
			
		||||
            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
 | 
			
		||||
                Utils.inToOut(is, out);
 | 
			
		||||
                ShellUtils.pump(is, out);
 | 
			
		||||
                md = out.toString();
 | 
			
		||||
                is.close();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
@@ -54,11 +54,13 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
 | 
			
		||||
        }
 | 
			
		||||
        String css;
 | 
			
		||||
        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()
 | 
			
		||||
        ) {
 | 
			
		||||
            Utils.inToOut(in, out);
 | 
			
		||||
            ShellUtils.pump(in, out);
 | 
			
		||||
            css = out.toString();
 | 
			
		||||
            in.close();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return "";
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,11 @@ import com.topjohnwu.magisk.FlashActivity;
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Const;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.WebService;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ZipUtils;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
import com.topjohnwu.superuser.ShellUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedInputStream;
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
@@ -68,7 +69,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                out.putNextEntry(new JarEntry(path));
 | 
			
		||||
                Utils.inToOut(in, out);
 | 
			
		||||
                ShellUtils.pump(in, out);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -107,7 +108,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
 | 
			
		||||
                InputStream in = new BufferedInputStream(new ProgressInputStream(conn.getInputStream()));
 | 
			
		||||
                OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))
 | 
			
		||||
            ) {
 | 
			
		||||
                Utils.inToOut(in, out);
 | 
			
		||||
                ShellUtils.pump(in, out);
 | 
			
		||||
                in.close();
 | 
			
		||||
            }
 | 
			
		||||
            conn.disconnect();
 | 
			
		||||
@@ -120,14 +121,8 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
 | 
			
		||||
            // First remove top folder in Github source zip, temp1 -> temp2
 | 
			
		||||
            removeTopFolder(temp1, temp2);
 | 
			
		||||
 | 
			
		||||
            // Then sign the zip for the first time, temp2 -> temp1
 | 
			
		||||
            ZipUtils.signZip(temp2, temp1, false);
 | 
			
		||||
 | 
			
		||||
            // Adjust the zip to prevent unzip issues, temp1 -> temp2
 | 
			
		||||
            ZipUtils.zipAdjust(temp1.getPath(), temp2.getPath());
 | 
			
		||||
 | 
			
		||||
            // Finally, sign the whole zip file again, temp2 -> target
 | 
			
		||||
            ZipUtils.signZip(temp2, mFile, true);
 | 
			
		||||
            // Then sign the zip
 | 
			
		||||
            ZipUtils.signZip(temp2, mFile);
 | 
			
		||||
 | 
			
		||||
            // Delete temp files
 | 
			
		||||
            temp1.delete();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,29 +4,24 @@ import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import com.topjohnwu.superuser.Shell;
 | 
			
		||||
import com.topjohnwu.superuser.ShellUtils;
 | 
			
		||||
 | 
			
		||||
public class RestoreImages extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Boolean doInBackground(Void... voids) {
 | 
			
		||||
        String sha1;
 | 
			
		||||
        List<String> ret = Utils.readFile("/.backup/.sha1");
 | 
			
		||||
        if (Utils.isValidShellResponse(ret)) {
 | 
			
		||||
            sha1 = ret.get(0);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = Shell.su("cat /init.magisk.rc | grep STOCKSHA1");
 | 
			
		||||
            if (!Utils.isValidShellResponse(ret))
 | 
			
		||||
        sha1 = Utils.cmd("cat /.backup/.sha1");
 | 
			
		||||
        if (sha1 == null) {
 | 
			
		||||
            sha1 = Utils.cmd("cat /init.magisk.rc | grep STOCKSHA1");
 | 
			
		||||
            if (sha1 == null)
 | 
			
		||||
                return false;
 | 
			
		||||
            sha1 = ret.get(0).substring(ret.get(0).indexOf('=') + 1);
 | 
			
		||||
            sha1 = sha1.substring(sha1.indexOf('=') + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = Shell.su("restore_imgs " + sha1 + " && echo true || echo false");
 | 
			
		||||
 | 
			
		||||
        return Utils.isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(ret.size() - 1));
 | 
			
		||||
        return ShellUtils.fastCmdResult(Shell.getShell(), "restore_imgs " + sha1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
        MagiskManager mm = MagiskManager.get();
 | 
			
		||||
        prefs = mm.prefs;
 | 
			
		||||
        repoDB = mm.repoDB;
 | 
			
		||||
        mm.repoLoadDone.hasPublished = false;
 | 
			
		||||
        mm.repoLoadDone.reset();
 | 
			
		||||
        // Legacy data cleanup
 | 
			
		||||
        File old = new File(mm.getApplicationInfo().dataDir + "/shared_prefs", "RepoMap.xml");
 | 
			
		||||
        if (old.exists() || prefs.getString("repomap", null) != null) {
 | 
			
		||||
@@ -160,6 +160,11 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
            ReposFragment.adapter.notifyDBChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPreExecute() {
 | 
			
		||||
        MagiskManager.get().repoLoadDone.setPending();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... voids) {
 | 
			
		||||
        etags = new ArrayList<>(Arrays.asList(prefs.getString(Const.Key.ETAG_KEY, "").split(",")));
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,9 @@ public abstract class Activity extends AppCompatActivity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @StyleRes
 | 
			
		||||
    abstract public int getDarkTheme();
 | 
			
		||||
    public int getDarkTheme() {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(@Nullable Bundle savedInstanceState) {
 | 
			
		||||
@@ -41,7 +43,7 @@ public abstract class Activity extends AppCompatActivity {
 | 
			
		||||
        if (this instanceof Topic.Subscriber) {
 | 
			
		||||
            ((Topic.Subscriber) this).subscribeTopics();
 | 
			
		||||
        }
 | 
			
		||||
        if (getMagiskManager().isDarkTheme && getDarkTheme() > 0) {
 | 
			
		||||
        if (getMagiskManager().isDarkTheme && getDarkTheme() != -1) {
 | 
			
		||||
            setTheme(getDarkTheme());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.container;
 | 
			
		||||
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 | 
			
		||||
public abstract class CallbackList<E> extends ArrayList<E> {
 | 
			
		||||
 | 
			
		||||
    private Handler handler;
 | 
			
		||||
 | 
			
		||||
    protected CallbackList() {
 | 
			
		||||
        handler = new Handler();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract void onAddElement(E e);
 | 
			
		||||
 | 
			
		||||
    public synchronized boolean add(E e) {
 | 
			
		||||
        boolean ret = super.add(e);
 | 
			
		||||
        handler.post(() -> onAddElement(e));
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user