mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-23 04:49:52 +00:00
Compare commits
91 Commits
manager-v5
...
manager-v5
Author | SHA1 | Date | |
---|---|---|---|
![]() |
49ba7ad22e | ||
![]() |
6ad33d60f7 | ||
![]() |
68c448bc34 | ||
![]() |
b885ccbd63 | ||
![]() |
da6f1d0f12 | ||
![]() |
4c0d435b6b | ||
![]() |
0e717a2de4 | ||
![]() |
cada862214 | ||
![]() |
682c6d4e7b | ||
![]() |
d0a253c97e | ||
![]() |
c0e2b3027b | ||
![]() |
e7dc14b07d | ||
![]() |
0da9146e90 | ||
![]() |
ad05a33e02 | ||
![]() |
8224e038a3 | ||
![]() |
03c04c2141 | ||
![]() |
2e091b04e5 | ||
![]() |
60296493fe | ||
![]() |
20c20f8f9b | ||
![]() |
f1d642a4e5 | ||
![]() |
e0e5ea17a4 | ||
![]() |
91a0ba72dc | ||
![]() |
c54c5a974a | ||
![]() |
532b8c54ab | ||
![]() |
5ac87891b5 | ||
![]() |
2d905ce3fb | ||
![]() |
831112abd2 | ||
![]() |
153d0f5505 | ||
![]() |
c78896a335 | ||
![]() |
316ec98e0f | ||
![]() |
cf58545a45 | ||
![]() |
e86015badc | ||
![]() |
c8f65fc9a1 | ||
![]() |
7684602ea8 | ||
![]() |
4601989d4a | ||
![]() |
23f697d62b | ||
![]() |
4ff39f8817 | ||
![]() |
1df41003ec | ||
![]() |
1f39ee41ad | ||
![]() |
42d8b1ecb9 | ||
![]() |
a4da7b33e6 | ||
![]() |
e4ee9e9095 | ||
![]() |
77430a282f | ||
![]() |
e6c1dd532d | ||
![]() |
d1f301e059 | ||
![]() |
1e812c40ce | ||
![]() |
a949641342 | ||
![]() |
c231e88a5d | ||
![]() |
79c71509f6 | ||
![]() |
5dab580cfc | ||
![]() |
499a157946 | ||
![]() |
c5a7ab2415 | ||
![]() |
3dd5a6f378 | ||
![]() |
7be26a0677 | ||
![]() |
c183fdd3ca | ||
![]() |
baa439457e | ||
![]() |
4dbcd54b72 | ||
![]() |
11062f2d4f | ||
![]() |
a0466085fe | ||
![]() |
f2f7d77847 | ||
![]() |
b2105f2d88 | ||
![]() |
4126f3bdcb | ||
![]() |
74ccfe6088 | ||
![]() |
48085b5573 | ||
![]() |
7b9ddc9b3b | ||
![]() |
15726a759c | ||
![]() |
2c7474ea87 | ||
![]() |
c726aee643 | ||
![]() |
c3e94e1480 | ||
![]() |
5f1343e5b4 | ||
![]() |
ffb1303d61 | ||
![]() |
a0b0d938f0 | ||
![]() |
158f5ba7d9 | ||
![]() |
b8cf40161c | ||
![]() |
fb96e6a56f | ||
![]() |
6668ba2511 | ||
![]() |
4668ef3020 | ||
![]() |
630f2b7d19 | ||
![]() |
dde0a4a7c8 | ||
![]() |
b06f69573d | ||
![]() |
8fd03f7434 | ||
![]() |
90e4ac2d23 | ||
![]() |
956bceae75 | ||
![]() |
c663be86de | ||
![]() |
aca78baecf | ||
![]() |
fbcf6b7954 | ||
![]() |
84123222aa | ||
![]() |
e9dbcf693d | ||
![]() |
1cd0a9d48f | ||
![]() |
1b48e44914 | ||
![]() |
0a398f03fd |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,7 +6,7 @@
|
|||||||
app/release
|
app/release
|
||||||
*.hprof
|
*.hprof
|
||||||
.externalNativeBuild/
|
.externalNativeBuild/
|
||||||
src/main/assets
|
src/full/res/raw/util_functions.sh
|
||||||
public.certificate.x509.pem
|
public.certificate.x509.pem
|
||||||
private.key.pk8
|
private.key.pk8
|
||||||
*.apk
|
*.apk
|
||||||
|
@@ -1,2 +1,7 @@
|
|||||||
# Magisk Manager
|
# Magisk Manager
|
||||||
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
|
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
The default (English) string resources are scattered in these files: `src/full/res/values/strings.xml`, `src/main/res/values/strings.xml`, `src/stub/res/values/strings.xml`.
|
||||||
|
Place the translated XMLs in the corresponding folder to the locale.
|
||||||
|
Translations are highly appreciated via pull requests here on Github.
|
||||||
|
35
build.gradle
35
build.gradle
@@ -8,8 +8,6 @@ android {
|
|||||||
applicationId "com.topjohnwu.magisk"
|
applicationId "com.topjohnwu.magisk"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion rootProject.ext.compileSdkVersion
|
targetSdkVersion rootProject.ext.compileSdkVersion
|
||||||
versionCode 115
|
|
||||||
versionName "5.7.0"
|
|
||||||
javaCompileOptions {
|
javaCompileOptions {
|
||||||
annotationProcessorOptions {
|
annotationProcessorOptions {
|
||||||
argument('butterknife.debuggable', 'false')
|
argument('butterknife.debuggable', 'false')
|
||||||
@@ -24,6 +22,20 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions "mode"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
full {
|
||||||
|
versionCode 127
|
||||||
|
versionName "5.8.1"
|
||||||
|
}
|
||||||
|
stub {
|
||||||
|
versionCode 1
|
||||||
|
versionName "stub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
@@ -39,14 +51,15 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation project(':utils')
|
fullImplementation project(':utils')
|
||||||
implementation 'com.github.topjohnwu:libsu:1.1.1'
|
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
|
||||||
implementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
|
||||||
implementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
|
||||||
implementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
|
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
|
||||||
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
|
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
|
||||||
implementation 'com.jakewharton:butterknife:8.8.1'
|
fullImplementation 'com.github.topjohnwu:libsu:1.3.0'
|
||||||
implementation 'com.atlassian.commonmark:commonmark:0.10.0'
|
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
|
||||||
implementation 'org.kamranzafar:jtar:2.3'
|
fullImplementation 'org.kamranzafar:jtar:2.3'
|
||||||
|
fullImplementation 'com.jakewharton:butterknife:8.8.1'
|
||||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
||||||
}
|
}
|
||||||
|
82
src/full/AndroidManifest.xml
Normal file
82
src/full/AndroidManifest.xml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.topjohnwu.magisk">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".MagiskManager"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
android:exported="true" />
|
||||||
|
<activity
|
||||||
|
android:name=".SplashActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/SplashTheme">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".AboutActivity"
|
||||||
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".SettingsActivity"
|
||||||
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".FlashActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:screenOrientation="nosensor"
|
||||||
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".NoUIActivity"
|
||||||
|
android:theme="@style/AppTheme.Translucent" />
|
||||||
|
<activity
|
||||||
|
android:name=".superuser.RequestActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity="internal.superuser"
|
||||||
|
android:theme="@style/SuRequest" />
|
||||||
|
|
||||||
|
<receiver android:name=".superuser.SuReceiver" />
|
||||||
|
<receiver android:name=".receivers.BootReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name=".receivers.PackageReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
|
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||||
|
<data android:scheme="package" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name=".receivers.ManagerUpdate" />
|
||||||
|
<receiver android:name=".receivers.RebootReceiver" />
|
||||||
|
<receiver android:name=".receivers.ShortcutReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<service android:name=".services.OnBootIntentService" />
|
||||||
|
<service
|
||||||
|
android:name=".services.UpdateCheckService"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
|
<!-- Hardcode GMS version -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.version"
|
||||||
|
android:value="7095000" />
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
@@ -31,7 +31,7 @@ public class AboutActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return R.style.AppTheme_Transparent_Dark;
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -17,6 +18,7 @@ import com.topjohnwu.magisk.asyncs.FlashZip;
|
|||||||
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.superuser.CallbackList;
|
import com.topjohnwu.superuser.CallbackList;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ public class FlashActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return R.style.AppTheme_Transparent_Dark;
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,13 +115,18 @@ public class FlashActivity extends Activity {
|
|||||||
case Const.Value.FLASH_ZIP:
|
case Const.Value.FLASH_ZIP:
|
||||||
new FlashZip(this, uri, console, logs).exec();
|
new FlashZip(this, uri, console, logs).exec();
|
||||||
break;
|
break;
|
||||||
case Const.Value.PATCH_BOOT:
|
case Const.Value.UNINSTALL:
|
||||||
new InstallMagisk(this, console, logs, uri, (Uri) intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT))
|
new UninstallMagisk(this, uri, console, logs).exec();
|
||||||
.exec();
|
|
||||||
break;
|
break;
|
||||||
case Const.Value.FLASH_MAGISK:
|
case Const.Value.FLASH_MAGISK:
|
||||||
new InstallMagisk(this, console, logs, uri, intent.getStringExtra(Const.Key.FLASH_SET_BOOT))
|
new InstallMagisk(this, console, logs, uri, InstallMagisk.DIRECT_MODE).exec();
|
||||||
.exec();
|
break;
|
||||||
|
case Const.Value.FLASH_SECOND_SLOT:
|
||||||
|
new InstallMagisk(this, console, logs, uri, InstallMagisk.SECOND_SLOT_MODE).exec();
|
||||||
|
break;
|
||||||
|
case Const.Value.PATCH_BOOT:
|
||||||
|
new InstallMagisk(this, console, logs, uri,
|
||||||
|
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,4 +135,21 @@ public class FlashActivity extends Activity {
|
|||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
// Prevent user accidentally press back button
|
// Prevent user accidentally press back button
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class UninstallMagisk extends FlashZip {
|
||||||
|
|
||||||
|
private UninstallMagisk(Activity context, Uri uri, List<String> console, List<String> logs) {
|
||||||
|
super(context, uri, console, logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Integer result) {
|
||||||
|
if (result == 1) {
|
||||||
|
new Handler().postDelayed(() ->
|
||||||
|
RootUtils.uninstallPkg(getActivity().getPackageName()), 3000);
|
||||||
|
} else {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -24,10 +24,12 @@ import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
|||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.Fragment;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
import com.topjohnwu.magisk.utils.ShowUI;
|
import com.topjohnwu.magisk.utils.ShowUI;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
import butterknife.BindColor;
|
import butterknife.BindColor;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
@@ -38,14 +40,6 @@ import butterknife.Unbinder;
|
|||||||
public class MagiskFragment extends Fragment
|
public class MagiskFragment extends Fragment
|
||||||
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
|
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
|
||||||
|
|
||||||
private static final int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
|
||||||
private static final int CAUSE_NETWORK_LOST = 0x02;
|
|
||||||
private static final int RESPONSE_ERR = 0x04;
|
|
||||||
private static final int CONNECTION_FAIL = 0x08;
|
|
||||||
|
|
||||||
private static final int BASIC_PASS = 0x10;
|
|
||||||
private static final int CTS_PASS = 0x20;
|
|
||||||
|
|
||||||
private Container expandableContainer = new Container();
|
private Container expandableContainer = new Container();
|
||||||
|
|
||||||
private MagiskManager mm;
|
private MagiskManager mm;
|
||||||
@@ -259,17 +253,22 @@ public class MagiskFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shownDialog && (mm.remoteMagiskVersionCode > mm.magiskVersionCode
|
|
||||||
|| mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE)) {
|
|
||||||
install();
|
|
||||||
}
|
|
||||||
|
|
||||||
magiskUpdateIcon.setImageResource(image);
|
magiskUpdateIcon.setImageResource(image);
|
||||||
magiskUpdateIcon.setColorFilter(color);
|
magiskUpdateIcon.setColorFilter(color);
|
||||||
magiskUpdateIcon.setVisibility(View.VISIBLE);
|
magiskUpdateIcon.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
magiskUpdateProgress.setVisibility(View.GONE);
|
magiskUpdateProgress.setVisibility(View.GONE);
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
|
||||||
|
if (!shownDialog) {
|
||||||
|
if (mm.remoteMagiskVersionCode > mm.magiskVersionCode
|
||||||
|
|| mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
|
install();
|
||||||
|
} else if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.FIX_ENV &&
|
||||||
|
!ShellUtils.fastCmdResult("env_check")) {
|
||||||
|
ShowUI.envFixDialog(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSafetyNetUI(int response) {
|
private void updateSafetyNetUI(int response) {
|
||||||
@@ -279,12 +278,12 @@ public class MagiskFragment extends Fragment
|
|||||||
safetyNetStatusText.setText(R.string.safetyNet_check_success);
|
safetyNetStatusText.setText(R.string.safetyNet_check_success);
|
||||||
|
|
||||||
boolean b;
|
boolean b;
|
||||||
b = (response & CTS_PASS) != 0;
|
b = (response & ISafetyNetHelper.CTS_PASS) != 0;
|
||||||
ctsStatusText.setText("ctsProfile: " + b);
|
ctsStatusText.setText("ctsProfile: " + b);
|
||||||
ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
||||||
ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
||||||
|
|
||||||
b = (response & BASIC_PASS) != 0;
|
b = (response & ISafetyNetHelper.BASIC_PASS) != 0;
|
||||||
basicStatusText.setText("basicIntegrity: " + b);
|
basicStatusText.setText("basicIntegrity: " + b);
|
||||||
basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
|
||||||
basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
|
||||||
@@ -293,16 +292,16 @@ public class MagiskFragment extends Fragment
|
|||||||
} else {
|
} else {
|
||||||
@StringRes int resid;
|
@StringRes int resid;
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case CAUSE_SERVICE_DISCONNECTED:
|
case ISafetyNetHelper.CAUSE_SERVICE_DISCONNECTED:
|
||||||
resid = R.string.safetyNet_network_loss;
|
resid = R.string.safetyNet_network_loss;
|
||||||
break;
|
break;
|
||||||
case CAUSE_NETWORK_LOST:
|
case ISafetyNetHelper.CAUSE_NETWORK_LOST:
|
||||||
resid = R.string.safetyNet_service_disconnected;
|
resid = R.string.safetyNet_service_disconnected;
|
||||||
break;
|
break;
|
||||||
case RESPONSE_ERR:
|
case ISafetyNetHelper.RESPONSE_ERR:
|
||||||
resid = R.string.safetyNet_res_invalid;
|
resid = R.string.safetyNet_res_invalid;
|
||||||
break;
|
break;
|
||||||
case CONNECTION_FAIL:
|
case ISafetyNetHelper.CONNECTION_FAIL:
|
||||||
default:
|
default:
|
||||||
resid = R.string.safetyNet_api_error;
|
resid = R.string.safetyNet_api_error;
|
||||||
break;
|
break;
|
@@ -28,7 +28,6 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
private ApplicationAdapter appAdapter;
|
private ApplicationAdapter appAdapter;
|
||||||
|
|
||||||
private SearchView.OnQueryTextListener searchListener;
|
private SearchView.OnQueryTextListener searchListener;
|
||||||
private String lastFilter;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
@@ -41,25 +40,22 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = ButterKnife.bind(this, view);
|
||||||
lastFilter = "";
|
|
||||||
|
|
||||||
mSwipeRefreshLayout.setRefreshing(true);
|
mSwipeRefreshLayout.setRefreshing(true);
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh());
|
mSwipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh());
|
||||||
|
|
||||||
appAdapter = new ApplicationAdapter(getActivity());
|
appAdapter = new ApplicationAdapter();
|
||||||
recyclerView.setAdapter(appAdapter);
|
recyclerView.setAdapter(appAdapter);
|
||||||
|
|
||||||
searchListener = new SearchView.OnQueryTextListener() {
|
searchListener = new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
lastFilter = query;
|
|
||||||
appAdapter.filter(query);
|
appAdapter.filter(query);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextChange(String newText) {
|
public boolean onQueryTextChange(String newText) {
|
||||||
lastFilter = newText;
|
|
||||||
appAdapter.filter(newText);
|
appAdapter.filter(newText);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -86,7 +82,7 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic) {
|
public void onTopicPublished(Topic topic) {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
appAdapter.filter(lastFilter);
|
appAdapter.filter(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
145
src/full/java/com/topjohnwu/magisk/MagiskLogFragment.java
Normal file
145
src/full/java/com/topjohnwu/magisk/MagiskLogFragment.java
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.Fragment;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.Unbinder;
|
||||||
|
|
||||||
|
public class MagiskLogFragment extends Fragment {
|
||||||
|
|
||||||
|
private Unbinder unbinder;
|
||||||
|
|
||||||
|
@BindView(R.id.txtLog) TextView txtLog;
|
||||||
|
@BindView(R.id.svLog) ScrollView svLog;
|
||||||
|
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
|
||||||
|
@BindView(R.id.progressBar) ProgressBar progressBar;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
|
||||||
|
unbinder = ButterKnife.bind(this, view);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
txtLog.setTextIsSelectable(true);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
getActivity().setTitle(R.string.log);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
readLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
unbinder.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.menu_log, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_refresh:
|
||||||
|
readLogs();
|
||||||
|
return true;
|
||||||
|
case R.id.menu_save:
|
||||||
|
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, this::saveLogs);
|
||||||
|
return true;
|
||||||
|
case R.id.menu_clear:
|
||||||
|
clearLogs();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readLogs() {
|
||||||
|
Shell.Async.su(new Shell.Async.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
if (ShellUtils.isValidOutput(out)) {
|
||||||
|
txtLog.setText(TextUtils.join("\n", out));
|
||||||
|
} else {
|
||||||
|
txtLog.setText(R.string.log_is_empty);
|
||||||
|
}
|
||||||
|
svLog.postDelayed(() -> svLog.fullScroll(ScrollView.FOCUS_DOWN), 100);
|
||||||
|
hsvLog.postDelayed(() -> hsvLog.fullScroll(ScrollView.FOCUS_LEFT), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskError(@NonNull Throwable throwable) {
|
||||||
|
txtLog.setText(R.string.log_is_empty);
|
||||||
|
}
|
||||||
|
}, "cat " + Const.MAGISK_LOG + " | tail -n 5000");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveLogs() {
|
||||||
|
Calendar now = Calendar.getInstance();
|
||||||
|
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
|
||||||
|
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||||
|
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||||
|
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||||
|
|
||||||
|
File targetFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
|
||||||
|
targetFile.getParentFile().mkdirs();
|
||||||
|
try {
|
||||||
|
targetFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Shell.Async.su(new Shell.Async.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
|
||||||
|
SnackbarMaker.make(txtLog, targetFile.getPath(), Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskError(@NonNull Throwable throwable) {}
|
||||||
|
}, "cat " + Const.MAGISK_LOG + " > " + targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearLogs() {
|
||||||
|
Shell.Async.su("echo -n > " + Const.MAGISK_LOG);
|
||||||
|
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
@@ -3,39 +3,41 @@ package com.topjohnwu.magisk;
|
|||||||
import android.app.job.JobInfo;
|
import android.app.job.JobInfo;
|
||||||
import android.app.job.JobScheduler;
|
import android.app.job.JobScheduler;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.Application;
|
||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.services.UpdateCheckService;
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
|
import com.topjohnwu.magisk.utils.ShellInitializer;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.BusyBox;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class MagiskManager extends Shell.ContainerApp {
|
public class MagiskManager extends Application implements Shell.Container {
|
||||||
|
|
||||||
// Global weak reference to self
|
|
||||||
private static WeakReference<MagiskManager> weakSelf;
|
|
||||||
|
|
||||||
// Topics
|
// Topics
|
||||||
public final Topic magiskHideDone = new Topic();
|
public final Topic magiskHideDone = new Topic();
|
||||||
@@ -52,12 +54,15 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
public int magiskVersionCode = -1;
|
public int magiskVersionCode = -1;
|
||||||
public String remoteMagiskVersionString;
|
public String remoteMagiskVersionString;
|
||||||
public int remoteMagiskVersionCode = -1;
|
public int remoteMagiskVersionCode = -1;
|
||||||
public String magiskLink;
|
|
||||||
public String releaseNoteLink;
|
|
||||||
public String remoteManagerVersionString;
|
public String remoteManagerVersionString;
|
||||||
public int remoteManagerVersionCode = -1;
|
public int remoteManagerVersionCode = -1;
|
||||||
|
|
||||||
|
public String magiskLink;
|
||||||
|
public String magiskNoteLink;
|
||||||
public String managerLink;
|
public String managerLink;
|
||||||
public String bootBlock = null;
|
public String managerNoteLink;
|
||||||
|
public String uninstallerLink;
|
||||||
|
|
||||||
public boolean keepVerity = false;
|
public boolean keepVerity = false;
|
||||||
public boolean keepEnc = false;
|
public boolean keepEnc = false;
|
||||||
|
|
||||||
@@ -65,10 +70,6 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
public Map<String, Module> moduleMap;
|
public Map<String, Module> moduleMap;
|
||||||
public List<Locale> locales;
|
public List<Locale> locales;
|
||||||
|
|
||||||
// Configurations
|
|
||||||
public static Locale locale;
|
|
||||||
public static Locale defaultLocale;
|
|
||||||
|
|
||||||
public boolean magiskHide;
|
public boolean magiskHide;
|
||||||
public boolean isDarkTheme;
|
public boolean isDarkTheme;
|
||||||
public int suRequestTimeout;
|
public int suRequestTimeout;
|
||||||
@@ -87,12 +88,23 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
public SharedPreferences prefs;
|
public SharedPreferences prefs;
|
||||||
public MagiskDatabaseHelper mDB;
|
public MagiskDatabaseHelper mDB;
|
||||||
public RepoDatabaseHelper repoDB;
|
public RepoDatabaseHelper repoDB;
|
||||||
public Runnable permissionGrantCallback = null;
|
|
||||||
|
|
||||||
private static Handler mHandler = new Handler();
|
private volatile Shell mShell;
|
||||||
|
|
||||||
public MagiskManager() {
|
public MagiskManager() {
|
||||||
weakSelf = new WeakReference<>(this);
|
weakSelf = new WeakReference<>(this);
|
||||||
|
Shell.setContainer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Shell getShell() {
|
||||||
|
return mShell;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShell(@Nullable Shell shell) {
|
||||||
|
mShell = shell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,52 +113,30 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
|
|
||||||
Shell.setFlags(Shell.FLAG_MOUNT_MASTER);
|
Shell.setFlags(Shell.FLAG_MOUNT_MASTER);
|
||||||
Shell.verboseLogging(BuildConfig.DEBUG);
|
Shell.verboseLogging(BuildConfig.DEBUG);
|
||||||
BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
|
Shell.setInitializer(ShellInitializer.class);
|
||||||
Shell.setInitializer(new Shell.Initializer() {
|
|
||||||
@Override
|
|
||||||
public void onRootShellInit(@NonNull Shell shell) {
|
|
||||||
try (InputStream utils = getAssets().open(Const.UTIL_FUNCTIONS);
|
|
||||||
InputStream magiskDB = getResources().openRawResource(R.raw.magiskdb)) {
|
|
||||||
shell.loadInputStream(null, null, utils);
|
|
||||||
shell.loadInputStream(null, null, magiskDB);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
shell.run(null, null,
|
|
||||||
"mount_partitions",
|
|
||||||
"run_migrations");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
// Handle duplicate package
|
|
||||||
if (!getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
|
||||||
try {
|
|
||||||
getPackageManager().getApplicationInfo(Const.ORIG_PKG_NAME, 0);
|
|
||||||
Intent intent = getPackageManager().getLaunchIntentForPackage(Const.ORIG_PKG_NAME);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
return;
|
|
||||||
} catch (PackageManager.NameNotFoundException ignored) { /* Expected */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
mDB = MagiskDatabaseHelper.getInstance(this);
|
mDB = MagiskDatabaseHelper.getInstance(this);
|
||||||
|
|
||||||
String pkg = mDB.getStrings(Const.Key.SU_REQUESTER, Const.ORIG_PKG_NAME);
|
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
|
||||||
if (getPackageName().equals(Const.ORIG_PKG_NAME) && !pkg.equals(Const.ORIG_PKG_NAME)) {
|
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
mDB.setStrings(Const.Key.SU_REQUESTER, null);
|
mDB.setStrings(Const.Key.SU_MANAGER, null);
|
||||||
Utils.uninstallPkg(pkg);
|
RootUtils.uninstallPkg(pkg);
|
||||||
mDB = MagiskDatabaseHelper.getInstance(this);
|
}
|
||||||
|
if (TextUtils.equals(pkg, getPackageName())) {
|
||||||
|
try {
|
||||||
|
// We are the manager, remove com.topjohnwu.magisk as it could be malware
|
||||||
|
getPackageManager().getApplicationInfo(Const.ORIG_PKG_NAME, 0);
|
||||||
|
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultLocale = Locale.getDefault();
|
|
||||||
setLocale();
|
setLocale();
|
||||||
loadConfig();
|
loadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MagiskManager get() {
|
public static MagiskManager get() {
|
||||||
return weakSelf.get();
|
return (MagiskManager) weakSelf.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocale() {
|
public void setLocale() {
|
||||||
@@ -182,7 +172,7 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
||||||
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
||||||
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE().exists())
|
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
|
||||||
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
||||||
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
||||||
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
||||||
@@ -198,36 +188,19 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void toast(CharSequence msg, int duration) {
|
|
||||||
mHandler.post(() -> Toast.makeText(weakSelf.get(), msg, duration).show());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void toast(int resId, int duration) {
|
|
||||||
mHandler.post(() -> Toast.makeText(weakSelf.get(), resId, duration).show());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadMagiskInfo() {
|
public void loadMagiskInfo() {
|
||||||
try {
|
try {
|
||||||
magiskVersionString = Utils.cmd("magisk -v").split(":")[0];
|
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
||||||
magiskVersionCode = Integer.parseInt(Utils.cmd("magisk -V"));
|
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
||||||
String s = Utils.cmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ? "resetprop -p " : "getprop ")
|
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
|
||||||
+ Const.MAGISKHIDE_PROP);
|
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
|
||||||
magiskHide = s == null || Integer.parseInt(s) != 0;
|
magiskHide = s == null || Integer.parseInt(s) != 0;
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
bootBlock = Utils.cmd("echo \"$BOOTIMAGE\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getDefaultInstallFlags() {
|
public void getDefaultInstallFlags() {
|
||||||
keepVerity = Boolean.parseBoolean(Utils.cmd("getvar KEEPVERITY; echo $KEEPVERITY")) ||
|
keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
||||||
Utils.cmd("echo \"$DTBOIMAGE\"") != null;
|
keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
||||||
|
|
||||||
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() {
|
public void setupUpdateCheck() {
|
||||||
@@ -248,4 +221,72 @@ public class MagiskManager extends Shell.ContainerApp {
|
|||||||
scheduler.cancel(Const.UPDATE_SERVICE_VER);
|
scheduler.cancel(Const.UPDATE_SERVICE_VER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dumpPrefs() {
|
||||||
|
// Flush prefs to disk
|
||||||
|
prefs.edit().commit();
|
||||||
|
File xml = new File(getFilesDir().getParent() + "/shared_prefs",
|
||||||
|
getPackageName() + "_preferences.xml");
|
||||||
|
Shell.Sync.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadPrefs() {
|
||||||
|
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
|
||||||
|
if (config.exists()) {
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
try {
|
||||||
|
SuFileInputStream is = new SuFileInputStream(config);
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||||
|
parser.setInput(is, "UTF-8");
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "map");
|
||||||
|
while (parser.next() != XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getEventType() != XmlPullParser.START_TAG)
|
||||||
|
continue;
|
||||||
|
String key = parser.getAttributeValue(null, "name");
|
||||||
|
String value = parser.getAttributeValue(null, "value");
|
||||||
|
switch (parser.getName()) {
|
||||||
|
case "string":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "string");
|
||||||
|
editor.putString(key, parser.nextText());
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "string");
|
||||||
|
break;
|
||||||
|
case "boolean":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "boolean");
|
||||||
|
editor.putBoolean(key, Boolean.parseBoolean(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "boolean");
|
||||||
|
break;
|
||||||
|
case "int":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int");
|
||||||
|
editor.putInt(key, Integer.parseInt(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int");
|
||||||
|
break;
|
||||||
|
case "long":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "long");
|
||||||
|
editor.putLong(key, Long.parseLong(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "long");
|
||||||
|
break;
|
||||||
|
case "float":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int");
|
||||||
|
editor.putFloat(key, Float.parseFloat(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
editor.remove(Const.Key.ETAG_KEY);
|
||||||
|
editor.apply();
|
||||||
|
loadConfig();
|
||||||
|
config.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -15,7 +15,6 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
@@ -91,12 +90,6 @@ public class MainActivity extends Activity
|
|||||||
navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
||||||
|
|
||||||
navigationView.setNavigationItemSelectedListener(this);
|
navigationView.setNavigationItemSelectedListener(this);
|
||||||
|
|
||||||
if (mm.prefs.getInt(Const.Key.APP_VER, -1) < BuildConfig.VERSION_CODE) {
|
|
||||||
mm.prefs.edit().putInt(Const.Key.APP_VER, BuildConfig.VERSION_CODE).apply();
|
|
||||||
new MarkDownWindow(this, getString(R.string.app_changelog),
|
|
||||||
getResources().openRawResource(R.raw.changelog)).exec();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -21,7 +21,6 @@ import com.topjohnwu.magisk.components.Fragment;
|
|||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -40,7 +39,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
|
|||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
@OnClick(R.id.fab)
|
@OnClick(R.id.fab)
|
||||||
public void selectFile() {
|
public void selectFile() {
|
||||||
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
|
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.setType("application/zip");
|
intent.setType("application/zip");
|
||||||
startActivityForResult(intent, Const.ID.FETCH_ZIP);
|
startActivityForResult(intent, Const.ID.FETCH_ZIP);
|
26
src/full/java/com/topjohnwu/magisk/NoUIActivity.java
Normal file
26
src/full/java/com/topjohnwu/magisk/NoUIActivity.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
|
||||||
|
public class NoUIActivity extends Activity {
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
String[] perms = getIntent().getStringArrayExtra(Const.Key.INTENT_PERM);
|
||||||
|
if (perms != null) {
|
||||||
|
ActivityCompat.requestPermissions(this, perms, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,33 +1,36 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.ListPreference;
|
import android.support.v14.preference.SwitchPreference;
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.PreferenceCategory;
|
|
||||||
import android.preference.PreferenceFragment;
|
|
||||||
import android.preference.PreferenceScreen;
|
|
||||||
import android.preference.SwitchPreference;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.preference.ListPreference;
|
||||||
|
import android.support.v7.preference.Preference;
|
||||||
|
import android.support.v7.preference.PreferenceCategory;
|
||||||
|
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||||
|
import android.support.v7.preference.PreferenceScreen;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.asyncs.HideManager;
|
import com.topjohnwu.magisk.asyncs.HideManager;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -41,7 +44,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return R.style.AppTheme_Transparent_Dark;
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,7 +66,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
setFloating();
|
setFloating();
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
getFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
|
getSupportFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -78,7 +81,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
return new Topic[] { getMagiskManager().reloadActivity };
|
return new Topic[] { getMagiskManager().reloadActivity };
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragment
|
public static class SettingsFragment extends PreferenceFragmentCompat
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
|
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
|
||||||
|
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
@@ -90,9 +93,8 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
private PreferenceCategory generalCatagory;
|
private PreferenceCategory generalCatagory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
super.onCreate(savedInstanceState);
|
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
||||||
addPreferencesFromResource(R.xml.app_settings);
|
|
||||||
mm = Utils.getMagiskManager(getActivity());
|
mm = Utils.getMagiskManager(getActivity());
|
||||||
prefs = mm.prefs;
|
prefs = mm.prefs;
|
||||||
prefScreen = getPreferenceScreen();
|
prefScreen = getPreferenceScreen();
|
||||||
@@ -126,13 +128,13 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
EditText url = v.findViewById(R.id.custom_url);
|
EditText url = v.findViewById(R.id.custom_url);
|
||||||
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setTitle(R.string.settings_update_custom)
|
.setTitle(R.string.settings_update_custom)
|
||||||
.setView(v)
|
.setView(v)
|
||||||
.setPositiveButton(R.string.ok, (d, i) ->
|
.setPositiveButton(R.string.ok, (d, i) ->
|
||||||
prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
|
prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
|
||||||
url.getText().toString()).apply())
|
url.getText().toString()).apply())
|
||||||
.setNegativeButton(R.string.close, null)
|
.setNegativeButton(R.string.close, null)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -166,14 +168,18 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
} else {
|
} else {
|
||||||
if (Utils.checkNetworkStatus()) {
|
if (Utils.checkNetworkStatus()) {
|
||||||
restoreManager.setOnPreferenceClickListener((pref) -> {
|
restoreManager.setOnPreferenceClickListener((pref) -> {
|
||||||
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
|
Utils.dlAndReceive(
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
getActivity(), new DownloadReceiver() {
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
@Override
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME,
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Utils.fmt("MagiskManager-v%s(%d).apk",
|
mm.dumpPrefs();
|
||||||
mm.remoteManagerVersionString, mm.remoteManagerVersionCode));
|
if (ShellUtils.fastCmdResult("pm install " + uri.getPath()))
|
||||||
mm.sendBroadcast(intent);
|
RootUtils.uninstallPkg(context.getPackageName());
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
mm.managerLink,
|
||||||
|
Utils.fmt("MagiskManager-v%s.apk", mm.remoteManagerVersionString)
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -200,13 +206,9 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setLocalePreference(ListPreference lp) {
|
private void setLocalePreference(ListPreference lp) {
|
||||||
boolean isNew = lp == null;
|
|
||||||
if (isNew) {
|
|
||||||
lp = new ListPreference(getActivity());
|
|
||||||
}
|
|
||||||
CharSequence[] entries = new CharSequence[mm.locales.size() + 1];
|
CharSequence[] entries = new CharSequence[mm.locales.size() + 1];
|
||||||
CharSequence[] entryValues = new CharSequence[mm.locales.size() + 1];
|
CharSequence[] entryValues = new CharSequence[mm.locales.size() + 1];
|
||||||
entries[0] = getString(R.string.system_default);
|
entries[0] = Utils.getLocaleString(MagiskManager.defaultLocale, R.string.system_default);
|
||||||
entryValues[0] = "";
|
entryValues[0] = "";
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (Locale locale : mm.locales) {
|
for (Locale locale : mm.locales) {
|
||||||
@@ -215,26 +217,21 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
lp.setEntries(entries);
|
lp.setEntries(entries);
|
||||||
lp.setEntryValues(entryValues);
|
lp.setEntryValues(entryValues);
|
||||||
lp.setTitle(R.string.language);
|
|
||||||
lp.setKey(Const.Key.LOCALE);
|
|
||||||
lp.setSummary(MagiskManager.locale.getDisplayName(MagiskManager.locale));
|
lp.setSummary(MagiskManager.locale.getDisplayName(MagiskManager.locale));
|
||||||
if (isNew) {
|
|
||||||
generalCatagory.addPreference(lp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
super.onResume();
|
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
subscribeTopics();
|
subscribeTopics();
|
||||||
|
return super.onCreateView(inflater, container, savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onDestroyView() {
|
||||||
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
unsubscribeTopics();
|
unsubscribeTopics();
|
||||||
super.onPause();
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -244,7 +241,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
case Const.Key.DARK_THEME:
|
case Const.Key.DARK_THEME:
|
||||||
mm.isDarkTheme = prefs.getBoolean(key, false);
|
mm.isDarkTheme = prefs.getBoolean(key, false);
|
||||||
mm.reloadActivity.publish(false);
|
mm.reloadActivity.publish(false);
|
||||||
break;
|
return;
|
||||||
case Const.Key.COREONLY:
|
case Const.Key.COREONLY:
|
||||||
if (prefs.getBoolean(key, false)) {
|
if (prefs.getBoolean(key, false)) {
|
||||||
try {
|
try {
|
||||||
@@ -265,12 +262,12 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
case Const.Key.HOSTS:
|
case Const.Key.HOSTS:
|
||||||
if (prefs.getBoolean(key, false)) {
|
if (prefs.getBoolean(key, false)) {
|
||||||
Shell.Async.su(
|
Shell.Async.su(
|
||||||
"cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE(),
|
"cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE,
|
||||||
"mount -o bind " + Const.MAGISK_HOST_FILE() + " /system/etc/hosts");
|
"mount -o bind " + Const.MAGISK_HOST_FILE + " /system/etc/hosts");
|
||||||
} else {
|
} else {
|
||||||
Shell.Async.su(
|
Shell.Async.su(
|
||||||
"umount -l /system/etc/hosts",
|
"umount -l /system/etc/hosts",
|
||||||
"rm -f " + Const.MAGISK_HOST_FILE());
|
"rm -f " + Const.MAGISK_HOST_FILE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Const.Key.ROOT_ACCESS:
|
case Const.Key.ROOT_ACCESS:
|
@@ -14,6 +14,7 @@ import com.topjohnwu.magisk.components.Activity;
|
|||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
@@ -23,12 +24,13 @@ public class SplashActivity extends Activity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
RootUtils.init();
|
||||||
MagiskManager mm = getMagiskManager();
|
MagiskManager mm = getMagiskManager();
|
||||||
|
|
||||||
mm.repoDB = new RepoDatabaseHelper(this);
|
mm.repoDB = new RepoDatabaseHelper(this);
|
||||||
mm.loadMagiskInfo();
|
mm.loadMagiskInfo();
|
||||||
mm.getDefaultInstallFlags();
|
mm.getDefaultInstallFlags();
|
||||||
Utils.loadPrefs();
|
mm.loadPrefs();
|
||||||
|
|
||||||
// Dynamic detect all locales
|
// Dynamic detect all locales
|
||||||
new LoadLocale().exec();
|
new LoadLocale().exec();
|
||||||
@@ -58,8 +60,6 @@ public class SplashActivity extends Activity {
|
|||||||
mm.setupUpdateCheck();
|
mm.setupUpdateCheck();
|
||||||
// Fire asynctasks
|
// Fire asynctasks
|
||||||
loadModuleTask.exec();
|
loadModuleTask.exec();
|
||||||
// Check dtbo status
|
|
||||||
Utils.patchDTBO();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write back default values
|
// Write back default values
|
@@ -1,6 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -13,11 +12,10 @@ import android.widget.Filter;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -30,25 +28,19 @@ import butterknife.ButterKnife;
|
|||||||
|
|
||||||
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
|
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
|
||||||
|
|
||||||
private List<ApplicationInfo> mOriginalList, mList;
|
private List<ApplicationInfo> fullList, showList;
|
||||||
private List<String> mHideList;
|
private List<String> hideList;
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private ApplicationFilter filter;
|
private ApplicationFilter filter;
|
||||||
private Topic magiskHideDone;
|
|
||||||
|
|
||||||
public ApplicationAdapter(Context context) {
|
public ApplicationAdapter() {
|
||||||
mOriginalList = mList = Collections.emptyList();
|
fullList = showList = Collections.emptyList();
|
||||||
mHideList = Collections.emptyList();
|
hideList = Collections.emptyList();
|
||||||
filter = new ApplicationFilter();
|
filter = new ApplicationFilter();
|
||||||
pm = context.getPackageManager();
|
pm = MagiskManager.get().getPackageManager();
|
||||||
magiskHideDone = Utils.getMagiskManager(context).magiskHideDone;
|
|
||||||
new LoadApps().exec();
|
new LoadApps().exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
|
|
||||||
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
|
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
|
||||||
@@ -57,28 +49,28 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||||
ApplicationInfo info = mList.get(position);
|
ApplicationInfo info = showList.get(position);
|
||||||
|
|
||||||
holder.appIcon.setImageDrawable(info.loadIcon(pm));
|
holder.appIcon.setImageDrawable(info.loadIcon(pm));
|
||||||
holder.appName.setText(info.loadLabel(pm));
|
holder.appName.setText(info.loadLabel(pm));
|
||||||
holder.appPackage.setText(info.packageName);
|
holder.appPackage.setText(info.packageName);
|
||||||
|
|
||||||
holder.checkBox.setOnCheckedChangeListener(null);
|
holder.checkBox.setOnCheckedChangeListener(null);
|
||||||
holder.checkBox.setChecked(mHideList.contains(info.packageName));
|
holder.checkBox.setChecked(hideList.contains(info.packageName));
|
||||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
Shell.Async.su("magiskhide --add " + info.packageName);
|
Shell.Async.su("magiskhide --add " + info.packageName);
|
||||||
mHideList.add(info.packageName);
|
hideList.add(info.packageName);
|
||||||
} else {
|
} else {
|
||||||
Shell.Async.su("magiskhide --rm " + info.packageName);
|
Shell.Async.su("magiskhide --rm " + info.packageName);
|
||||||
mHideList.remove(info.packageName);
|
hideList.remove(info.packageName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return mList.size();
|
return showList.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void filter(String constraint) {
|
public void filter(String constraint) {
|
||||||
@@ -104,17 +96,21 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
private class ApplicationFilter extends Filter {
|
private class ApplicationFilter extends Filter {
|
||||||
|
|
||||||
|
private boolean lowercaseContains(String s, CharSequence filter) {
|
||||||
|
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FilterResults performFiltering(CharSequence constraint) {
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
if (constraint == null || constraint.length() == 0) {
|
if (constraint == null || constraint.length() == 0) {
|
||||||
mList = mOriginalList;
|
showList = fullList;
|
||||||
} else {
|
} else {
|
||||||
mList = new ArrayList<>();
|
showList = new ArrayList<>();
|
||||||
String filter = constraint.toString().toLowerCase();
|
String filter = constraint.toString().toLowerCase();
|
||||||
for (ApplicationInfo info : mOriginalList) {
|
for (ApplicationInfo info : fullList) {
|
||||||
if (lowercaseContains(info.loadLabel(pm), filter)
|
if (lowercaseContains(info.loadLabel(pm).toString(), filter)
|
||||||
|| lowercaseContains(info.packageName, filter)) {
|
|| lowercaseContains(info.packageName, filter)) {
|
||||||
mList.add(info);
|
showList.add(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,22 +127,32 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
mOriginalList = pm.getInstalledApplications(0);
|
fullList = pm.getInstalledApplications(0);
|
||||||
for (Iterator<ApplicationInfo> i = mOriginalList.iterator(); i.hasNext(); ) {
|
hideList = Shell.Sync.su("magiskhide --ls");
|
||||||
|
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
|
||||||
ApplicationInfo info = i.next();
|
ApplicationInfo info = i.next();
|
||||||
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
|
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
|
||||||
i.remove();
|
i.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(mOriginalList, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
|
Collections.sort(fullList, (a, b) -> {
|
||||||
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
|
boolean ah = hideList.contains(a.packageName);
|
||||||
mHideList = Shell.Sync.su("magiskhide --ls");
|
boolean bh = hideList.contains(b.packageName);
|
||||||
|
if (ah == bh) {
|
||||||
|
return a.loadLabel(pm).toString().toLowerCase().compareTo(
|
||||||
|
b.loadLabel(pm).toString().toLowerCase());
|
||||||
|
} else if (ah) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void v) {
|
protected void onPostExecute(Void v) {
|
||||||
magiskHideDone.publish(false);
|
MagiskManager.get().magiskHideDone.publish(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,6 +4,7 @@ import android.app.Activity;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
@@ -12,10 +13,8 @@ import java.io.BufferedInputStream;
|
|||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
import dalvik.system.DexClassLoader;
|
import dalvik.system.DexClassLoader;
|
||||||
@@ -24,32 +23,33 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
|
|
||||||
public static final File dexPath =
|
public static final File dexPath =
|
||||||
new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
|
new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
|
||||||
private DexClassLoader loader;
|
private ISafetyNetHelper helper;
|
||||||
private Class<?> helperClazz, callbackClazz;
|
|
||||||
|
|
||||||
public CheckSafetyNet(Activity activity) {
|
public CheckSafetyNet(Activity activity) {
|
||||||
super(activity);
|
super(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dlSnet() throws IOException {
|
private void dlSnet() throws Exception {
|
||||||
Shell.Sync.sh("rm -rf " + dexPath.getParent());
|
Shell.Sync.sh("rm -rf " + dexPath.getParent());
|
||||||
HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
|
|
||||||
dexPath.getParentFile().mkdir();
|
dexPath.getParentFile().mkdir();
|
||||||
|
HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
|
||||||
try (
|
try (
|
||||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
|
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
|
||||||
InputStream in = new BufferedInputStream(conn.getInputStream())) {
|
InputStream in = new BufferedInputStream(conn.getInputStream())) {
|
||||||
ShellUtils.pump(in, out);
|
ShellUtils.pump(in, out);
|
||||||
|
} finally {
|
||||||
|
conn.disconnect();
|
||||||
}
|
}
|
||||||
conn.disconnect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dyload() throws Exception {
|
private void dyload() throws Exception {
|
||||||
loader = new DexClassLoader(dexPath.getPath(), dexPath.getParent(),
|
DexClassLoader loader = new DexClassLoader(dexPath.getPath(), dexPath.getParent(),
|
||||||
null, ClassLoader.getSystemClassLoader());
|
null, ISafetyNetHelper.class.getClassLoader());
|
||||||
helperClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetHelper");
|
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.SafetyNetHelper");
|
||||||
callbackClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetCallback");
|
helper = (ISafetyNetHelper) clazz.getConstructors()[0]
|
||||||
int snet_ver = (int) helperClazz.getMethod("getVersion").invoke(null);
|
.newInstance(getActivity(), (ISafetyNetHelper.Callback)
|
||||||
if (snet_ver != Const.SNET_VER) {
|
code -> MagiskManager.get().safetyNetDone.publish(false, code));
|
||||||
|
if (helper.getVersion() != Const.SNET_VER) {
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,21 +72,13 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Exception err) {
|
protected void onPostExecute(Exception e) {
|
||||||
MagiskManager mm = MagiskManager.get();
|
if (e == null) {
|
||||||
try {
|
helper.attest();
|
||||||
if (err != null) throw err;
|
} else {
|
||||||
Object helper = helperClazz.getConstructors()[0].newInstance(
|
|
||||||
getActivity(), dexPath.getPath(), Proxy.newProxyInstance(
|
|
||||||
loader, new Class[] { callbackClazz }, (proxy, method, args) -> {
|
|
||||||
mm.safetyNetDone.publish(false, args[0]);
|
|
||||||
return null;
|
|
||||||
}));
|
|
||||||
helperClazz.getMethod("attest").invoke(helper);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
mm.safetyNetDone.publish(false, -1);
|
MagiskManager.get().safetyNetDone.publish(false, -1);
|
||||||
}
|
}
|
||||||
super.onPostExecute(err);
|
super.onPostExecute(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -42,11 +42,14 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
|||||||
mm.remoteMagiskVersionString = magisk.getString("version");
|
mm.remoteMagiskVersionString = magisk.getString("version");
|
||||||
mm.remoteMagiskVersionCode = magisk.getInt("versionCode");
|
mm.remoteMagiskVersionCode = magisk.getInt("versionCode");
|
||||||
mm.magiskLink = magisk.getString("link");
|
mm.magiskLink = magisk.getString("link");
|
||||||
mm.releaseNoteLink = magisk.getString("note");
|
mm.magiskNoteLink = magisk.getString("note");
|
||||||
JSONObject manager = json.getJSONObject("app");
|
JSONObject manager = json.getJSONObject("app");
|
||||||
mm.remoteManagerVersionString = manager.getString("version");
|
mm.remoteManagerVersionString = manager.getString("version");
|
||||||
mm.remoteManagerVersionCode = manager.getInt("versionCode");
|
mm.remoteManagerVersionCode = manager.getInt("versionCode");
|
||||||
mm.managerLink = manager.getString("link");
|
mm.managerLink = manager.getString("link");
|
||||||
|
mm.managerNoteLink = manager.getString("note");
|
||||||
|
JSONObject uninstaller = json.getJSONObject("uninstaller");
|
||||||
|
mm.uninstallerLink = uninstaller.getString("link");
|
||||||
} catch (JSONException ignored) {}
|
} catch (JSONException ignored) {}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
@@ -7,6 +7,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
@@ -39,8 +40,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
|
|
||||||
private boolean unzipAndCheck() throws Exception {
|
private boolean unzipAndCheck() throws Exception {
|
||||||
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
|
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
|
||||||
String s = Utils.cmd("head -n 1 " + new File(mCachedFile.getParentFile(), "updater-script"));
|
return ShellUtils.fastCmdResult("grep -q '#MAGISK' " + new File(mCachedFile.getParentFile(), "updater-script"));
|
||||||
return s != null && s.contains("#MAGISK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,7 +93,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
switch (result) {
|
switch (result) {
|
||||||
case -1:
|
case -1:
|
||||||
console.add("! Installation failed");
|
console.add("! Installation failed");
|
||||||
Utils.showUriSnack(getActivity(), mUri);
|
SnackbarMaker.showUri(getActivity(), mUri);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
console.add("! This zip is not a Magisk Module!");
|
console.add("! This zip is not a Magisk Module!");
|
95
src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java
Normal file
95
src/full/java/com/topjohnwu/magisk/asyncs/HideManager.java
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.PatchAPK;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
public class HideManager extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
|
||||||
|
public HideManager(Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String genPackageName(String prefix, int length) {
|
||||||
|
StringBuilder builder = new StringBuilder(length);
|
||||||
|
builder.append(prefix);
|
||||||
|
length -= prefix.length();
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
String base = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
String alpha = base + base.toUpperCase();
|
||||||
|
String full = alpha + "0123456789..........";
|
||||||
|
char next, prev = '\0';
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
if (prev == '.' || i == length - 1 || i == 0) {
|
||||||
|
next = alpha.charAt(random.nextInt(alpha.length()));
|
||||||
|
} else {
|
||||||
|
next = full.charAt(random.nextInt(full.length()));
|
||||||
|
}
|
||||||
|
builder.append(next);
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
dialog = ProgressDialog.show(getActivity(),
|
||||||
|
getActivity().getString(R.string.hide_manager_toast),
|
||||||
|
getActivity().getString(R.string.hide_manager_toast2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
MagiskManager mm = MagiskManager.get();
|
||||||
|
|
||||||
|
// Generate a new app with random package name
|
||||||
|
SuFile repack = new SuFile("/data/local/tmp/repack.apk");
|
||||||
|
String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!PatchAPK.patchPackageID(
|
||||||
|
mm.getPackageCodePath(),
|
||||||
|
new SuFileOutputStream(repack),
|
||||||
|
Const.ORIG_PKG_NAME, pkg))
|
||||||
|
return false;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install the application
|
||||||
|
if (!ShellUtils.fastCmdResult(Shell.getShell(), "pm install " + repack))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
repack.delete();
|
||||||
|
|
||||||
|
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
|
||||||
|
mm.dumpPrefs();
|
||||||
|
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean b) {
|
||||||
|
dialog.dismiss();
|
||||||
|
if (!b) {
|
||||||
|
MagiskManager.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
super.onPostExecute(b);
|
||||||
|
}
|
||||||
|
}
|
327
src/full/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
Normal file
327
src/full/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.container.TarEntry;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
import com.topjohnwu.utils.SignBoot;
|
||||||
|
|
||||||
|
import org.kamranzafar.jtar.TarInputStream;
|
||||||
|
import org.kamranzafar.jtar.TarOutputStream;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
private static final int PATCH_MODE = 0;
|
||||||
|
public static final int DIRECT_MODE = 1;
|
||||||
|
private static final int FIX_ENV_MODE = 2;
|
||||||
|
public static final int SECOND_SLOT_MODE = 3;
|
||||||
|
|
||||||
|
private Uri bootUri, mZip;
|
||||||
|
private List<String> console, logs;
|
||||||
|
private String mBoot;
|
||||||
|
private int mode;
|
||||||
|
private File installDir;
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
private MagiskManager mm;
|
||||||
|
|
||||||
|
public InstallMagisk(Activity context, Uri zip) {
|
||||||
|
super(context);
|
||||||
|
mZip = zip;
|
||||||
|
mm = MagiskManager.get();
|
||||||
|
mode = FIX_ENV_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstallMagisk(Activity context, List<String> console, List<String> logs, Uri zip, int mode) {
|
||||||
|
this(context, zip);
|
||||||
|
this.console = console;
|
||||||
|
this.logs = logs;
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri zip, Uri boot) {
|
||||||
|
this(context, console, logs, zip, PATCH_MODE);
|
||||||
|
bootUri = boot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
Activity a = getActivity();
|
||||||
|
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
|
||||||
|
console = new NOPList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractFiles(String arch) throws IOException {
|
||||||
|
console.add("- Extracting files");
|
||||||
|
try (InputStream in = mm.getContentResolver().openInputStream(mZip)) {
|
||||||
|
if (in == null) throw new FileNotFoundException();
|
||||||
|
BufferedInputStream buf = new BufferedInputStream(in);
|
||||||
|
buf.mark(Integer.MAX_VALUE);
|
||||||
|
ZipUtils.unzip(buf, installDir, arch + "/", true);
|
||||||
|
buf.reset();
|
||||||
|
ZipUtils.unzip(buf, installDir, "common/", true);
|
||||||
|
buf.reset();
|
||||||
|
ZipUtils.unzip(buf, installDir, "chromeos/", false);
|
||||||
|
buf.reset();
|
||||||
|
ZipUtils.unzip(buf, installDir, "META-INF/com/google/android/update-binary", true);
|
||||||
|
buf.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
console.add("! Invalid Uri");
|
||||||
|
throw e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
console.add("! Cannot unzip zip");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
Shell.Sync.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
|
||||||
|
installDir, installDir, installDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean dumpBoot() {
|
||||||
|
console.add("- Copying image locally");
|
||||||
|
// Copy boot image to local
|
||||||
|
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
|
||||||
|
OutputStream out = new FileOutputStream(mBoot)
|
||||||
|
) {
|
||||||
|
if (in == null)
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
|
||||||
|
InputStream src;
|
||||||
|
if (Utils.getNameFromUri(mm, bootUri).endsWith(".tar")) {
|
||||||
|
// Extract boot.img from tar
|
||||||
|
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
|
||||||
|
org.kamranzafar.jtar.TarEntry entry;
|
||||||
|
while ((entry = tar.getNextEntry()) != null) {
|
||||||
|
if (entry.getName().equals("boot.img"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
src = tar;
|
||||||
|
} else {
|
||||||
|
// Direct copy raw image
|
||||||
|
src = new BufferedInputStream(in);
|
||||||
|
}
|
||||||
|
ShellUtils.pump(src, out);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
console.add("! Invalid Uri");
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
console.add("! Copy failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File patchBoot() throws IOException {
|
||||||
|
boolean isSigned;
|
||||||
|
try (InputStream in = new SuFileInputStream(mBoot)) {
|
||||||
|
isSigned = SignBoot.verifySignature(in, null);
|
||||||
|
if (isSigned) {
|
||||||
|
console.add("- Boot image is signed with AVB 1.0");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
console.add("! Unable to check signature");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch boot image
|
||||||
|
Shell.Sync.sh(console, logs,
|
||||||
|
"cd " + installDir,
|
||||||
|
Utils.fmt("KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep " +
|
||||||
|
"boot_patch.sh %s || echo 'Failed!'",
|
||||||
|
mm.keepEnc, mm.keepVerity, mBoot));
|
||||||
|
|
||||||
|
if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Shell.Sync.sh("mv bin/busybox busybox",
|
||||||
|
"rm -rf magisk.apk bin boot.img update-binary",
|
||||||
|
"cd /");
|
||||||
|
|
||||||
|
File patched = new File(installDir, "new-boot.img");
|
||||||
|
if (isSigned) {
|
||||||
|
console.add("- Signing boot image with test keys");
|
||||||
|
File signed = new File(installDir, "signed.img");
|
||||||
|
try (InputStream in = new SuFileInputStream(patched);
|
||||||
|
OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))
|
||||||
|
) {
|
||||||
|
SignBoot.doSignature("/boot", in, out, null, null);
|
||||||
|
}
|
||||||
|
Shell.Sync.su("mv -f " + signed + " " + patched);
|
||||||
|
}
|
||||||
|
return patched;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void outputBoot(File patched) throws IOException {
|
||||||
|
switch (mode) {
|
||||||
|
case PATCH_MODE:
|
||||||
|
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + mm.bootFormat);
|
||||||
|
dest.getParentFile().mkdirs();
|
||||||
|
OutputStream out;
|
||||||
|
switch (mm.bootFormat) {
|
||||||
|
case ".img.tar":
|
||||||
|
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
|
||||||
|
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case ".img":
|
||||||
|
out = new BufferedOutputStream(new FileOutputStream(dest));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try (InputStream in = new SuFileInputStream(patched)) {
|
||||||
|
ShellUtils.pump(in, out);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
Shell.Sync.su("rm -f " + patched);
|
||||||
|
console.add("");
|
||||||
|
console.add("****************************");
|
||||||
|
console.add(" Patched image is placed in ");
|
||||||
|
console.add(" " + dest + " ");
|
||||||
|
console.add("****************************");
|
||||||
|
break;
|
||||||
|
case SECOND_SLOT_MODE:
|
||||||
|
case DIRECT_MODE:
|
||||||
|
Shell.Sync.sh(console, logs,
|
||||||
|
Utils.fmt("direct_install %s %s %s", patched, mBoot, installDir));
|
||||||
|
if (!mm.keepVerity)
|
||||||
|
Shell.Sync.sh(console, logs, "find_dtbo_image", "patch_dtbo_image");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
installDir = new File("/data/adb/magisk");
|
||||||
|
Shell.Sync.sh("rm -rf /data/adb/magisk/*");
|
||||||
|
} else {
|
||||||
|
installDir = new File(
|
||||||
|
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
|
||||||
|
mm.createDeviceProtectedStorageContext() : mm)
|
||||||
|
.getFilesDir().getParent()
|
||||||
|
, "install");
|
||||||
|
Shell.Sync.sh("rm -rf " + installDir);
|
||||||
|
installDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case PATCH_MODE:
|
||||||
|
mBoot = new File(installDir, "boot.img").getAbsolutePath();
|
||||||
|
if (!dumpBoot())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case DIRECT_MODE:
|
||||||
|
console.add("- Detecting target image");
|
||||||
|
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
|
||||||
|
break;
|
||||||
|
case SECOND_SLOT_MODE:
|
||||||
|
console.add("- Detecting target image");
|
||||||
|
char slot[] = ShellUtils.fastCmd("echo $SLOT").toCharArray();
|
||||||
|
if (slot[1] == 'a') slot[1] = 'b';
|
||||||
|
else slot[1] = 'a';
|
||||||
|
mBoot = ShellUtils.fastCmd("SLOT=" + String.valueOf(slot),
|
||||||
|
"find_boot_image", "echo \"$BOOTIMAGE\"");
|
||||||
|
Shell.Async.su("mount_partitions");
|
||||||
|
break;
|
||||||
|
case FIX_ENV_MODE:
|
||||||
|
mBoot = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mBoot == null) {
|
||||||
|
console.add("! Unable to detect target image");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.add("- Target image: " + mBoot);
|
||||||
|
|
||||||
|
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
|
||||||
|
String arch;
|
||||||
|
|
||||||
|
if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.SEPOL_REFACTOR) {
|
||||||
|
// 32-bit only
|
||||||
|
if (abis.contains("x86")) arch = "x86";
|
||||||
|
else arch = "arm";
|
||||||
|
} else {
|
||||||
|
if (abis.contains("x86_64")) arch = "x64";
|
||||||
|
else if (abis.contains("arm64-v8a")) arch = "arm64";
|
||||||
|
else if (abis.contains("x86")) arch = "x86";
|
||||||
|
else arch = "arm";
|
||||||
|
}
|
||||||
|
|
||||||
|
console.add("- Device platform: " + Build.SUPPORTED_ABIS[0]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
extractFiles(arch);
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
Shell.Sync.sh("fix_env");
|
||||||
|
} else {
|
||||||
|
File patched = patchBoot();
|
||||||
|
if (patched == null)
|
||||||
|
return false;
|
||||||
|
outputBoot(patched);
|
||||||
|
console.add("- All done!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
if (mode == FIX_ENV_MODE) {
|
||||||
|
dialog.dismiss();
|
||||||
|
MagiskManager.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
|
||||||
|
} else {
|
||||||
|
// Running in FlashActivity
|
||||||
|
FlashActivity activity = (FlashActivity) getActivity();
|
||||||
|
if (!result) {
|
||||||
|
Shell.Async.sh("rm -rf " + installDir);
|
||||||
|
console.add("! Installation failed");
|
||||||
|
activity.reboot.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
activity.buttonPanel.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NOPList<E> extends AbstractList<E> {
|
||||||
|
@Override
|
||||||
|
public E get(int index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, E element) {}
|
||||||
|
}
|
||||||
|
}
|
@@ -11,7 +11,7 @@ import java.util.List;
|
|||||||
public class LoadModules extends ParallelTask<Void, Void, Void> {
|
public class LoadModules extends ParallelTask<Void, Void, Void> {
|
||||||
|
|
||||||
private List<String> getModList() {
|
private List<String> getModList() {
|
||||||
String command = "ls -d " + Const.MAGISK_PATH() + "/* | grep -v lost+found";
|
String command = "ls -d " + Const.MAGISK_PATH + "/* | grep -v lost+found";
|
||||||
return Shell.Sync.su(command);
|
return Shell.Sync.su(command);
|
||||||
}
|
}
|
||||||
|
|
@@ -12,8 +12,8 @@ import android.widget.Toast;
|
|||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
@@ -90,7 +90,6 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
do {
|
do {
|
||||||
conn = WebService.request(mLink, null);
|
conn = WebService.request(mLink, null);
|
||||||
if (conn == null) return null;
|
|
||||||
total = conn.getContentLength();
|
total = conn.getContentLength();
|
||||||
if (total < 0)
|
if (total < 0)
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
@@ -147,7 +146,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
Utils.showUriSnack(activity, uri);
|
SnackbarMaker.showUri(activity, uri);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MagiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
|
MagiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
|
||||||
@@ -157,7 +156,8 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParallelTask<Void, Object, Boolean> exec(Void... voids) {
|
public ParallelTask<Void, Object, Boolean> exec(Void... voids) {
|
||||||
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
com.topjohnwu.magisk.components.Activity.runWithPermission(
|
||||||
|
getActivity(), new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
|
||||||
() -> super.exec(voids));
|
() -> super.exec(voids));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
@@ -1,31 +1,35 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
public class RestoreImages extends ParallelTask<Void, Void, Boolean> {
|
public class RestoreImages extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
private ProgressDialog dialog;
|
||||||
|
|
||||||
|
public RestoreImages(Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Activity a = getActivity();
|
||||||
|
dialog = ProgressDialog.show(a, a.getString(R.string.restore_img), a.getString(R.string.restore_img_msg));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... voids) {
|
protected Boolean doInBackground(Void... voids) {
|
||||||
String sha1;
|
return ShellUtils.fastCmdResult("restore_imgs");
|
||||||
sha1 = Utils.cmd("cat /.backup/.sha1");
|
|
||||||
if (sha1 == null) {
|
|
||||||
sha1 = Utils.cmd("cat /init.magisk.rc | grep STOCKSHA1");
|
|
||||||
if (sha1 == null)
|
|
||||||
return false;
|
|
||||||
sha1 = sha1.substring(sha1.indexOf('=') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShellUtils.fastCmdResult(Shell.getShell(), "restore_imgs " + sha1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
|
dialog.cancel();
|
||||||
if (result) {
|
if (result) {
|
||||||
MagiskManager.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
MagiskManager.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
||||||
} else {
|
} else {
|
@@ -1,28 +1,34 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.ReposFragment;
|
import com.topjohnwu.magisk.ReposFragment;
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Logger;
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
||||||
@@ -33,7 +39,8 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||||
|
|
||||||
private MagiskManager mm;
|
private MagiskManager mm;
|
||||||
private List<String> cached, etags, newEtags = new ArrayList<>();
|
private List<String> etags, newEtags = new LinkedList<>();
|
||||||
|
private Set<String> cached;
|
||||||
private boolean forceUpdate;
|
private boolean forceUpdate;
|
||||||
private AtomicInteger taskCount = new AtomicInteger(0);
|
private AtomicInteger taskCount = new AtomicInteger(0);
|
||||||
final private Object allDone = new Object();
|
final private Object allDone = new Object();
|
||||||
@@ -41,13 +48,6 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
public UpdateRepos(boolean force) {
|
public UpdateRepos(boolean force) {
|
||||||
mm = MagiskManager.get();
|
mm = MagiskManager.get();
|
||||||
mm.repoLoadDone.reset();
|
mm.repoLoadDone.reset();
|
||||||
// Legacy data cleanup
|
|
||||||
File old = new File(mm.getApplicationInfo().dataDir + "/shared_prefs", "RepoMap.xml");
|
|
||||||
if (old.exists() || mm.prefs.getString("repomap", null) != null) {
|
|
||||||
old.delete();
|
|
||||||
mm.prefs.edit().remove("version").remove("repomap").remove(Const.Key.ETAG_KEY).apply();
|
|
||||||
mm.repoDB.clearRepo();
|
|
||||||
}
|
|
||||||
forceUpdate = force;
|
forceUpdate = force;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,78 +80,68 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadJSON(String jsonString) throws Exception {
|
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
|
||||||
JSONArray jsonArray = new JSONArray(jsonString);
|
JSONArray jsonArray = new JSONArray(jsonString);
|
||||||
|
|
||||||
// Empty page, throw error
|
// Empty page, halt
|
||||||
if (jsonArray.length() == 0)
|
if (jsonArray.length() == 0)
|
||||||
throw new Exception();
|
return false;
|
||||||
|
|
||||||
for (int i = 0; i < jsonArray.length(); i++) {
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
||||||
String id = rawRepo.getString("description");
|
String id = rawRepo.getString("description");
|
||||||
String name = rawRepo.getString("name");
|
String name = rawRepo.getString("name");
|
||||||
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
|
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
|
||||||
|
Set<String> set = Collections.synchronizedSet(cached);
|
||||||
queueTask(() -> {
|
queueTask(() -> {
|
||||||
Repo repo = mm.repoDB.getRepo(id);
|
Repo repo = mm.repoDB.getRepo(id);
|
||||||
Boolean updated;
|
|
||||||
try {
|
try {
|
||||||
if (repo == null) {
|
if (repo == null)
|
||||||
repo = new Repo(name, date);
|
repo = new Repo(name);
|
||||||
updated = true;
|
else
|
||||||
} else {
|
set.remove(id);
|
||||||
// Popout from cached
|
repo.update(date);
|
||||||
cached.remove(id);
|
mm.repoDB.addRepo(repo);
|
||||||
if (forceUpdate) {
|
publishProgress();
|
||||||
repo.update();
|
|
||||||
updated = true;
|
|
||||||
} else {
|
|
||||||
updated = repo.update(date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (updated) {
|
|
||||||
mm.repoDB.addRepo(repo);
|
|
||||||
publishProgress();
|
|
||||||
}
|
|
||||||
if (!id.equals(repo.getId())) {
|
|
||||||
Logger.error("Repo [" + name + "] id=[" + repo.getId() + "] has illegal repo id");
|
|
||||||
}
|
|
||||||
} catch (Repo.IllegalRepoException e) {
|
} catch (Repo.IllegalRepoException e) {
|
||||||
Logger.error(e.getMessage());
|
Logger.debug(e.getMessage());
|
||||||
mm.repoDB.removeRepo(id);
|
mm.repoDB.removeRepo(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadPage(int page, int mode) {
|
private boolean loadPage(int page, int mode) {
|
||||||
Map<String, String> header = new HashMap<>();
|
Map<String, String> header = new HashMap<>();
|
||||||
String etag = "";
|
if (mode == CHECK_ETAG && page < etags.size())
|
||||||
if (mode == CHECK_ETAG && page < etags.size()) {
|
header.put(Const.Key.IF_NONE_MATCH, etags.get(page));
|
||||||
etag = etags.get(page);
|
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
|
||||||
}
|
|
||||||
header.put(Const.Key.IF_NONE_MATCH, etag);
|
|
||||||
String url = String.format(Locale.US, Const.Url.REPO_URL, page + 1);
|
|
||||||
HttpURLConnection conn = WebService.request(url, header);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (conn == null)
|
HttpURLConnection conn = WebService.request(url, header);
|
||||||
throw new Exception();
|
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||||
newEtags.add(etag);
|
// Current page is not updated, check the next page
|
||||||
return page + 1 < etags.size() && loadPage(page + 1, CHECK_ETAG);
|
return loadPage(page + 1, CHECK_ETAG);
|
||||||
}
|
}
|
||||||
loadJSON(WebService.getString(conn));
|
if (!loadJSON(WebService.getString(conn)))
|
||||||
|
return mode != CHECK_ETAG;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
// Don't continue
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If one page is updated, we force update all pages */
|
||||||
|
|
||||||
// Update ETAG
|
// Update ETAG
|
||||||
etag = header.get(Const.Key.ETAG_KEY);
|
String etag = header.get(Const.Key.ETAG_KEY);
|
||||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||||
newEtags.add(etag);
|
if (mode == LOAD_PREV) {
|
||||||
|
// We are loading a previous page, push the new tag to the front
|
||||||
|
newEtags.add(0, etag);
|
||||||
|
} else {
|
||||||
|
newEtags.add(etag);
|
||||||
|
}
|
||||||
|
|
||||||
String links = header.get(Const.Key.LINK_KEY);
|
String links = header.get(Const.Key.LINK_KEY);
|
||||||
if (links != null) {
|
if (links != null) {
|
||||||
@@ -159,7 +149,8 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
if (mode != LOAD_PREV && s.contains("next")) {
|
if (mode != LOAD_PREV && s.contains("next")) {
|
||||||
// Force load all next pages
|
// Force load all next pages
|
||||||
loadPage(page + 1, LOAD_NEXT);
|
loadPage(page + 1, LOAD_NEXT);
|
||||||
} else if (mode != LOAD_NEXT && s.contains("prev")) {
|
}
|
||||||
|
if (mode != LOAD_NEXT && s.contains("prev")) {
|
||||||
// Back propagation
|
// Back propagation
|
||||||
loadPage(page - 1, LOAD_PREV);
|
loadPage(page - 1, LOAD_PREV);
|
||||||
}
|
}
|
||||||
@@ -181,40 +172,32 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
etags = new ArrayList<>(Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(",")));
|
etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(","));
|
||||||
cached = mm.repoDB.getRepoIDList();
|
cached = mm.repoDB.getRepoIDSet();
|
||||||
|
|
||||||
if (!loadPage(0, CHECK_ETAG)) {
|
if (loadPage(0, CHECK_ETAG)) {
|
||||||
// Nothing changed online
|
|
||||||
if (forceUpdate) {
|
|
||||||
for (String id : cached) {
|
|
||||||
if (id == null) continue;
|
|
||||||
queueTask(() -> {
|
|
||||||
Repo repo = mm.repoDB.getRepo(id);
|
|
||||||
try {
|
|
||||||
repo.update();
|
|
||||||
mm.repoDB.addRepo(repo);
|
|
||||||
} catch (Repo.IllegalRepoException e) {
|
|
||||||
Logger.error(e.getMessage());
|
|
||||||
mm.repoDB.removeRepo(repo);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
waitTasks();
|
|
||||||
} else {
|
|
||||||
waitTasks();
|
waitTasks();
|
||||||
|
|
||||||
// The leftover cached means they are removed from online repo
|
// The leftover cached means they are removed from online repo
|
||||||
mm.repoDB.removeRepo(cached);
|
mm.repoDB.removeRepo(cached);
|
||||||
|
|
||||||
// Update ETag
|
// Update ETag
|
||||||
StringBuilder etagBuilder = new StringBuilder();
|
mm.prefs.edit().putString(Const.Key.ETAG_KEY, TextUtils.join(",", newEtags)).apply();
|
||||||
for (int i = 0; i < newEtags.size(); ++i) {
|
} else if (forceUpdate) {
|
||||||
if (i != 0) etagBuilder.append(",");
|
Cursor c = mm.repoDB.getRawCursor();
|
||||||
etagBuilder.append(newEtags.get(i));
|
while (c.moveToNext()) {
|
||||||
|
Repo repo = new Repo(c);
|
||||||
|
queueTask(() -> {
|
||||||
|
try {
|
||||||
|
repo.update();
|
||||||
|
mm.repoDB.addRepo(repo);
|
||||||
|
} catch (Repo.IllegalRepoException e) {
|
||||||
|
Logger.debug(e.getMessage());
|
||||||
|
mm.repoDB.removeRepo(repo);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etagBuilder.toString()).apply();
|
waitTasks();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
@@ -0,0 +1,108 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Keep;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.StyleRes;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
|
public abstract class FlavorActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private AssetManager swappedAssetManager = null;
|
||||||
|
private Resources swappedResources = null;
|
||||||
|
private Resources.Theme backupTheme = null;
|
||||||
|
|
||||||
|
@StyleRes
|
||||||
|
public int getDarkTheme() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MagiskManager getMagiskManager() {
|
||||||
|
return (MagiskManager) super.getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (this instanceof Topic.Subscriber) {
|
||||||
|
((Topic.Subscriber) this).subscribeTopics();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getMagiskManager().isDarkTheme && getDarkTheme() != -1) {
|
||||||
|
setTheme(getDarkTheme());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
if (this instanceof Topic.Subscriber) {
|
||||||
|
((Topic.Subscriber) this).unsubscribeTopics();
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setFloating() {
|
||||||
|
boolean isTablet = getResources().getBoolean(R.bool.isTablet);
|
||||||
|
if (isTablet) {
|
||||||
|
WindowManager.LayoutParams params = getWindow().getAttributes();
|
||||||
|
params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
|
||||||
|
params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
|
||||||
|
params.alpha = 1.0f;
|
||||||
|
params.dimAmount = 0.6f;
|
||||||
|
params.flags |= 2;
|
||||||
|
getWindow().setAttributes(params);
|
||||||
|
setFinishOnTouchOutside(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resources.Theme getTheme() {
|
||||||
|
return backupTheme == null ? super.getTheme() : backupTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssetManager getAssets() {
|
||||||
|
return swappedAssetManager == null ? super.getAssets() : swappedAssetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AssetManager getAssets(String apk) {
|
||||||
|
try {
|
||||||
|
AssetManager asset = AssetManager.class.newInstance();
|
||||||
|
AssetManager.class.getMethod("addAssetPath", String.class).invoke(asset, apk);
|
||||||
|
return asset;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resources getResources() {
|
||||||
|
return swappedResources == null ? super.getResources() : swappedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public void swapResources(String dexPath) {
|
||||||
|
AssetManager asset = getAssets(dexPath);
|
||||||
|
if (asset != null) {
|
||||||
|
backupTheme = super.getTheme();
|
||||||
|
Resources res = super.getResources();
|
||||||
|
swappedResources = new Resources(asset, res.getDisplayMetrics(), res.getConfiguration());
|
||||||
|
swappedAssetManager = asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public void restoreResources() {
|
||||||
|
swappedAssetManager = null;
|
||||||
|
swappedResources = null;
|
||||||
|
backupTheme = null;
|
||||||
|
}
|
||||||
|
}
|
@@ -36,4 +36,8 @@ public class Fragment extends android.support.v4.app.Fragment {
|
|||||||
public void startActivityForResult(Intent intent, int requestCode, Activity.ActivityResultListener listener) {
|
public void startActivityForResult(Intent intent, int requestCode, Activity.ActivityResultListener listener) {
|
||||||
((Activity) getActivity()).startActivityForResult(intent, requestCode, listener);
|
((Activity) getActivity()).startActivityForResult(intent, requestCode, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void runWithPermission(String[] permissions, Runnable callback) {
|
||||||
|
Activity.runWithPermission(getActivity(), permissions, callback);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,11 +1,15 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.net.Uri;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
public class SnackbarMaker {
|
public class SnackbarMaker {
|
||||||
|
|
||||||
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
||||||
@@ -34,4 +38,10 @@ public class SnackbarMaker {
|
|||||||
text.setMaxLines(Integer.MAX_VALUE);
|
text.setMaxLines(Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showUri(Activity activity, Uri uri) {
|
||||||
|
make(activity, activity.getString(R.string.internal_storage,
|
||||||
|
"/MagiskManager/" + Utils.getNameFromUri(activity, uri)),
|
||||||
|
Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.ok, (v)->{}).show();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -3,8 +3,6 @@ package com.topjohnwu.magisk.container;
|
|||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class Module extends BaseModule {
|
public class Module extends BaseModule {
|
||||||
|
|
||||||
private SuFile mRemoveFile, mDisableFile, mUpdateFile;
|
private SuFile mRemoveFile, mDisableFile, mUpdateFile;
|
||||||
@@ -13,12 +11,12 @@ public class Module extends BaseModule {
|
|||||||
public Module(String path) {
|
public Module(String path) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parseProps(Shell.Sync.su("cat " + path + "/module.prop"));
|
parseProps(Shell.Sync.su("dos2unix < " + path + "/module.prop"));
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {}
|
||||||
|
|
||||||
mRemoveFile = new SuFile(path + "/remove", true);
|
mRemoveFile = new SuFile(path + "/remove");
|
||||||
mDisableFile = new SuFile(path + "/disable", true);
|
mDisableFile = new SuFile(path + "/disable");
|
||||||
mUpdateFile = new SuFile(path + "/update", true);
|
mUpdateFile = new SuFile(path + "/update");
|
||||||
|
|
||||||
if (getId() == null) {
|
if (getId() == null) {
|
||||||
int sep = path.lastIndexOf('/');
|
int sep = path.lastIndexOf('/');
|
||||||
@@ -36,9 +34,7 @@ public class Module extends BaseModule {
|
|||||||
|
|
||||||
public void createDisableFile() {
|
public void createDisableFile() {
|
||||||
mEnable = false;
|
mEnable = false;
|
||||||
try {
|
mDisableFile.createNewFile();
|
||||||
mDisableFile.createNewFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDisableFile() {
|
public void removeDisableFile() {
|
||||||
@@ -52,9 +48,7 @@ public class Module extends BaseModule {
|
|||||||
|
|
||||||
public void createRemoveFile() {
|
public void createRemoveFile() {
|
||||||
mRemove = true;
|
mRemove = true;
|
||||||
try {
|
mRemoveFile.createNewFile();
|
||||||
mRemoveFile.createNewFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteRemoveFile() {
|
public void deleteRemoveFile() {
|
@@ -20,7 +20,8 @@ public class Policy implements Comparable<Policy>{
|
|||||||
|
|
||||||
public Policy(int uid, PackageManager pm) throws PackageManager.NameNotFoundException {
|
public Policy(int uid, PackageManager pm) throws PackageManager.NameNotFoundException {
|
||||||
String[] pkgs = pm.getPackagesForUid(uid);
|
String[] pkgs = pm.getPackagesForUid(uid);
|
||||||
if (pkgs == null || pkgs.length == 0) throw new PackageManager.NameNotFoundException();
|
if (pkgs == null || pkgs.length == 0)
|
||||||
|
throw new PackageManager.NameNotFoundException();
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
packageName = pkgs[0];
|
packageName = pkgs[0];
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
@@ -35,6 +36,8 @@ public class Policy implements Comparable<Policy>{
|
|||||||
logging = c.getInt(c.getColumnIndex("logging")) != 0;
|
logging = c.getInt(c.getColumnIndex("logging")) != 0;
|
||||||
notification = c.getInt(c.getColumnIndex("notification")) != 0;
|
notification = c.getInt(c.getColumnIndex("notification")) != 0;
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
|
if (info.uid != uid)
|
||||||
|
throw new PackageManager.NameNotFoundException();
|
||||||
appName = info.loadLabel(pm).toString();
|
appName = info.loadLabel(pm).toString();
|
||||||
}
|
}
|
||||||
|
|
@@ -5,6 +5,8 @@ import android.database.Cursor;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
@@ -15,10 +17,8 @@ public class Repo extends BaseModule {
|
|||||||
private String repoName;
|
private String repoName;
|
||||||
private Date mLastUpdate;
|
private Date mLastUpdate;
|
||||||
|
|
||||||
public Repo(String name, Date lastUpdate) throws IllegalRepoException {
|
public Repo(String name) {
|
||||||
mLastUpdate = lastUpdate;
|
|
||||||
repoName = name;
|
repoName = name;
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Repo(Cursor c) {
|
public Repo(Cursor c) {
|
||||||
@@ -28,10 +28,9 @@ public class Repo extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update() throws IllegalRepoException {
|
public void update() throws IllegalRepoException {
|
||||||
String props = WebService.getString(getManifestUrl());
|
String props[] = Utils.dos2unix(WebService.getString(getManifestUrl())).split("\\n");
|
||||||
String lines[] = props.split("\\n");
|
|
||||||
try {
|
try {
|
||||||
parseProps(lines);
|
parseProps(props);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
|
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -43,17 +42,13 @@ public class Repo extends BaseModule {
|
|||||||
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
||||||
}
|
}
|
||||||
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
|
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] is outdated");
|
Logger.debug("Repo [" + repoName + "] is outdated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean update(Date lastUpdate) throws IllegalRepoException {
|
public void update(Date lastUpdate) throws IllegalRepoException {
|
||||||
if (lastUpdate.after(mLastUpdate)) {
|
mLastUpdate = lastUpdate;
|
||||||
mLastUpdate = lastUpdate;
|
update();
|
||||||
update();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -78,7 +78,7 @@ public class MagiskDatabaseHelper {
|
|||||||
if (mm.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
|
if (mm.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
|
||||||
// Super old legacy mode
|
// Super old legacy mode
|
||||||
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
||||||
} else if (mm.magiskVersionCode < Const.MAGISK_VER.LEGACY_GLOBAL_DB) {
|
} else if (mm.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
|
||||||
// Legacy mode with FBE aware
|
// Legacy mode with FBE aware
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
de.moveDatabaseFrom(mm, "su.db");
|
de.moveDatabaseFrom(mm, "su.db");
|
||||||
@@ -86,16 +86,9 @@ public class MagiskDatabaseHelper {
|
|||||||
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
||||||
} else {
|
} else {
|
||||||
// Global database
|
// Global database
|
||||||
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db", true);
|
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
|
||||||
mm.deleteDatabase("su.db");
|
mm.deleteDatabase("su.db");
|
||||||
de.deleteDatabase("su.db");
|
de.deleteDatabase("su.db");
|
||||||
if (mm.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
|
|
||||||
// Link to new path
|
|
||||||
File oldDB = new File(de.getFilesDir().getParentFile().getParentFile(),
|
|
||||||
"magisk.db");
|
|
||||||
Shell.Sync.su(Utils.fmt("mv -f %s %s; ln -s %s %s",
|
|
||||||
oldDB, GLOBAL_DB, GLOBAL_DB, oldDB));
|
|
||||||
}
|
|
||||||
if (mm.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
|
if (mm.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
|
||||||
// We need some additional policies on old versions
|
// We need some additional policies on old versions
|
||||||
Shell.Sync.su("db_sepatch");
|
Shell.Sync.su("db_sepatch");
|
||||||
@@ -105,8 +98,8 @@ public class MagiskDatabaseHelper {
|
|||||||
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
|
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
|
||||||
Shell.Sync.su("db_restore");
|
Shell.Sync.su("db_restore");
|
||||||
}
|
}
|
||||||
Shell.Sync.su("db_setup " + Process.myUid());
|
|
||||||
}
|
}
|
||||||
|
Shell.Sync.su("db_setup " + Process.myUid());
|
||||||
}
|
}
|
||||||
// Not using legacy mode, open the mounted global DB
|
// Not using legacy mode, open the mounted global DB
|
||||||
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
|
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
|
@@ -10,8 +10,8 @@ import com.topjohnwu.magisk.container.Repo;
|
|||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
|
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeRepo(List<String> list) {
|
public void removeRepo(Iterable<String> list) {
|
||||||
for (String id : list) {
|
for (String id : list) {
|
||||||
if (id == null) continue;
|
if (id == null) continue;
|
||||||
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
||||||
@@ -94,6 +94,10 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cursor getRawCursor() {
|
||||||
|
return mDb.query(TABLE_NAME, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public Cursor getRepoCursor() {
|
public Cursor getRepoCursor() {
|
||||||
String orderBy = null;
|
String orderBy = null;
|
||||||
switch (mm.repoOrder) {
|
switch (mm.repoOrder) {
|
||||||
@@ -103,18 +107,18 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
case Const.Value.ORDER_DATE:
|
case Const.Value.ORDER_DATE:
|
||||||
orderBy = "last_update DESC";
|
orderBy = "last_update DESC";
|
||||||
}
|
}
|
||||||
return mDb.query(TABLE_NAME, null, "minMagisk<=?",
|
return mDb.query(TABLE_NAME, null, "minMagisk<=? AND minMagisk>=?",
|
||||||
new String[] { String.valueOf(mm.magiskVersionCode) },
|
new String[] { String.valueOf(mm.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER()) },
|
||||||
null, null, orderBy);
|
null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getRepoIDList() {
|
public Set<String> getRepoIDSet() {
|
||||||
LinkedList<String> ret = new LinkedList<>();
|
HashSet<String> set = new HashSet<>(300);
|
||||||
try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) {
|
try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
ret.add(c.getString(c.getColumnIndex("id")));
|
set.add(c.getString(c.getColumnIndex("id")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return set;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,7 +9,8 @@ import com.topjohnwu.magisk.services.OnBootIntentService;
|
|||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
public class BootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private void startIntentService(Context context) {
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
context.startForegroundService(new Intent(context, OnBootIntentService.class));
|
context.startForegroundService(new Intent(context, OnBootIntentService.class));
|
||||||
} else {
|
} else {
|
||||||
@@ -17,9 +18,4 @@ public class BootReceiver extends BroadcastReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
startIntentService(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
package com.topjohnwu.magisk.receivers;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.PatchAPK;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
public class ManagerUpdate extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Utils.dlAndReceive(
|
||||||
|
context, new PatchedInstall(),
|
||||||
|
intent.getStringExtra(Const.Key.INTENT_SET_LINK),
|
||||||
|
intent.getStringExtra(Const.Key.INTENT_SET_FILENAME)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PatchedInstall extends ManagerInstall {
|
||||||
|
@Override
|
||||||
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
|
if (!context.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
String o = uri.getPath();
|
||||||
|
String p = o.substring(0, o.lastIndexOf('.')) + "-patched.apk";
|
||||||
|
try {
|
||||||
|
PatchAPK.patchPackageID(o, new BufferedOutputStream(new FileOutputStream(p)),
|
||||||
|
Const.ORIG_PKG_NAME, context.getPackageName());
|
||||||
|
} catch (FileNotFoundException ignored) { }
|
||||||
|
super.onDownloadDone(context, Uri.fromFile(new File(p)));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
super.onDownloadDone(context, uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,8 +8,7 @@ import android.support.v4.app.NotificationCompat;
|
|||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
public class OnBootIntentService extends IntentService {
|
public class OnBootIntentService extends IntentService {
|
||||||
|
|
||||||
@@ -21,12 +20,12 @@ public class OnBootIntentService extends IntentService {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationCompat.Builder builder =
|
startForeground(Const.ID.ONBOOT_NOTIFICATION_ID,
|
||||||
new NotificationCompat.Builder(this, Const.ID.NOTIFICATION_CHANNEL);
|
new NotificationCompat.Builder(this, Const.ID.NOTIFICATION_CHANNEL)
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle("onBoot")
|
.setContentTitle("Startup Operations")
|
||||||
.setContentText("Running onBoot operations...");
|
.setContentText("Running startup operations...")
|
||||||
startForeground(Const.ID.ONBOOT_NOTIFICATION_ID, builder.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +38,7 @@ public class OnBootIntentService extends IntentService {
|
|||||||
* Check for dtbo status every boot time, and prompt user
|
* Check for dtbo status every boot time, and prompt user
|
||||||
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
||||||
* */
|
* */
|
||||||
MagiskManager mm = Utils.getMagiskManager(this);
|
MagiskManager.get().loadMagiskInfo();
|
||||||
mm.loadMagiskInfo();
|
RootUtils.patchDTBO();
|
||||||
mm.getDefaultInstallFlags();
|
|
||||||
if (Shell.rootAccess()) {
|
|
||||||
Utils.patchDTBO();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -277,6 +277,10 @@ public class RequestActivity extends Activity {
|
|||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
policy = new Policy(uid, pm);
|
policy = new Policy(uid, pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Never allow com.topjohnwu.magisk (could be malware) */
|
||||||
|
if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME))
|
||||||
|
return false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
@@ -0,0 +1,22 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.support.annotation.Keep;
|
||||||
|
|
||||||
|
public interface ISafetyNetHelper {
|
||||||
|
|
||||||
|
int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
||||||
|
int CAUSE_NETWORK_LOST = 0x02;
|
||||||
|
int RESPONSE_ERR = 0x04;
|
||||||
|
int CONNECTION_FAIL = 0x08;
|
||||||
|
|
||||||
|
int BASIC_PASS = 0x10;
|
||||||
|
int CTS_PASS = 0x20;
|
||||||
|
|
||||||
|
void attest();
|
||||||
|
int getVersion();
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
@Keep
|
||||||
|
void onResponse(int responseCode);
|
||||||
|
}
|
||||||
|
}
|
27
src/full/java/com/topjohnwu/magisk/utils/Logger.java
Normal file
27
src/full/java/com/topjohnwu/magisk/utils/Logger.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class Logger {
|
||||||
|
|
||||||
|
public static void debug(String line) {
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(Const.DEBUG_TAG, "DEBUG: " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void debug(String fmt, Object... args) {
|
||||||
|
debug(Utils.fmt(fmt, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(String line) {
|
||||||
|
Log.e(Const.DEBUG_TAG, "ERROR: " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(String fmt, Object... args) {
|
||||||
|
error(Utils.fmt(fmt, args));
|
||||||
|
}
|
||||||
|
}
|
77
src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java
Normal file
77
src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import com.topjohnwu.utils.JarMap;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
|
||||||
|
public class PatchAPK {
|
||||||
|
|
||||||
|
private static int findOffset(byte buf[], byte pattern[]) {
|
||||||
|
int offset = -1;
|
||||||
|
for (int i = 0; i < buf.length - pattern.length; ++i) {
|
||||||
|
boolean match = true;
|
||||||
|
for (int j = 0; j < pattern.length; ++j) {
|
||||||
|
if (buf[i + j] != pattern[j]) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
offset = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It seems that AAPT sometimes generate another type of string format */
|
||||||
|
private static boolean fallbackPatch(byte xml[], String from, String to) {
|
||||||
|
|
||||||
|
byte[] target = new byte[from.length() * 2 + 2];
|
||||||
|
for (int i = 0; i < from.length(); ++i) {
|
||||||
|
target[i * 2] = (byte) from.charAt(i);
|
||||||
|
}
|
||||||
|
int offset = findOffset(xml, target);
|
||||||
|
if (offset < 0)
|
||||||
|
return false;
|
||||||
|
byte[] dest = new byte[target.length - 2];
|
||||||
|
for (int i = 0; i < to.length(); ++i) {
|
||||||
|
dest[i * 2] = (byte) to.charAt(i);
|
||||||
|
}
|
||||||
|
System.arraycopy(dest, 0, xml, offset, dest.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean findAndPatch(byte xml[], String from, String to) {
|
||||||
|
byte target[] = (from + '\0').getBytes();
|
||||||
|
int offset = findOffset(xml, target);
|
||||||
|
if (offset < 0)
|
||||||
|
return fallbackPatch(xml, from, to);
|
||||||
|
System.arraycopy(to.getBytes(), 0, xml, offset, to.length());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean patchPackageID(String fileName, OutputStream out, String from, String to) {
|
||||||
|
try {
|
||||||
|
JarMap apk = new JarMap(fileName);
|
||||||
|
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
|
||||||
|
byte xml[] = apk.getRawData(je);
|
||||||
|
|
||||||
|
if (!findAndPatch(xml, from, to))
|
||||||
|
return false;
|
||||||
|
if (!findAndPatch(xml, from + ".provider", to + ".provider"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Write in changes
|
||||||
|
apk.getOutputStream(je).write(xml);
|
||||||
|
|
||||||
|
// Sign the APK
|
||||||
|
ZipUtils.signZip(apk, out);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
38
src/full/java/com/topjohnwu/magisk/utils/RootUtils.java
Normal file
38
src/full/java/com/topjohnwu/magisk/utils/RootUtils.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
|
public class RootUtils {
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
if (Shell.rootAccess()) {
|
||||||
|
Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
|
||||||
|
SuFile file = new SuFile("/sbin/.core/img");
|
||||||
|
if (file.exists()) {
|
||||||
|
Const.MAGISK_PATH = file;
|
||||||
|
} else if ((file = new SuFile("/dev/magisk/img")).exists()) {
|
||||||
|
Const.MAGISK_PATH = file;
|
||||||
|
} else {
|
||||||
|
Const.MAGISK_PATH = new SuFile("/magisk");
|
||||||
|
}
|
||||||
|
Const.MAGISK_HOST_FILE = new SuFile(Const.MAGISK_PATH + "/.core/hosts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uninstallPkg(String pkg) {
|
||||||
|
Shell.Sync.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void patchDTBO() {
|
||||||
|
if (Shell.rootAccess()) {
|
||||||
|
MagiskManager mm = MagiskManager.get();
|
||||||
|
if (mm.magiskVersionCode >= Const.MAGISK_VER.DTBO_SUPPORT) {
|
||||||
|
if (Boolean.parseBoolean(ShellUtils.fastCmd("mm_patch_dtbo")))
|
||||||
|
ShowUI.dtboPatchedNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.superuser.BusyBox;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class ShellInitializer extends Shell.Initializer {
|
||||||
|
|
||||||
|
static {
|
||||||
|
BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onRootShellInit(Context context, @NonNull Shell shell) throws Exception {
|
||||||
|
try (InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions);
|
||||||
|
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils)
|
||||||
|
) {
|
||||||
|
shell.execTask((in, err, out) -> {
|
||||||
|
ShellUtils.pump(magiskUtils, in);
|
||||||
|
ShellUtils.pump(managerUtils, in);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
shell.run(null, null,
|
||||||
|
"mount_partitions",
|
||||||
|
"get_flags",
|
||||||
|
"run_migrations");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -7,27 +7,26 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.SplashActivity;
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
import com.topjohnwu.magisk.asyncs.RestoreImages;
|
import com.topjohnwu.magisk.asyncs.RestoreImages;
|
||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||||
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
||||||
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -45,7 +44,7 @@ public class ShowUI {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle(mm.getString(R.string.magisk_update_title))
|
.setContentTitle(mm.getString(R.string.magisk_update_title))
|
||||||
.setContentText(mm.getString(R.string.magisk_update_available, mm.remoteMagiskVersionString))
|
.setContentText(mm.getString(R.string.magisk_update_available, mm.remoteMagiskVersionString))
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
@@ -69,7 +68,7 @@ public class ShowUI {
|
|||||||
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle(mm.getString(R.string.manager_update_title))
|
.setContentTitle(mm.getString(R.string.manager_update_title))
|
||||||
.setContentText(mm.getString(R.string.manager_download_install))
|
.setContentText(mm.getString(R.string.manager_download_install))
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
@@ -89,7 +88,7 @@ public class ShowUI {
|
|||||||
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
|
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
|
||||||
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
|
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
@@ -100,11 +99,31 @@ public class ShowUI {
|
|||||||
notificationManager.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
|
notificationManager.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void magiskInstallDialog(Activity activity) {
|
public static void envFixDialog(Activity activity) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
||||||
new AlertDialogBuilder(activity)
|
new AlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.env_fix_title)
|
||||||
|
.setMessage(R.string.env_fix_msg)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setPositiveButton(R.string.yes, (d, i) -> {
|
||||||
|
Utils.dlAndReceive(activity, new DownloadReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
|
new InstallMagisk(activity, uri).exec();
|
||||||
|
}
|
||||||
|
}, mm.magiskLink, filename);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void magiskInstallDialog(Activity activity) {
|
||||||
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
|
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
||||||
|
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
||||||
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)))
|
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)))
|
||||||
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
@@ -115,17 +134,11 @@ public class ShowUI {
|
|||||||
if (Shell.rootAccess()) {
|
if (Shell.rootAccess()) {
|
||||||
options.add(mm.getString(R.string.direct_install));
|
options.add(mm.getString(R.string.direct_install));
|
||||||
}
|
}
|
||||||
String s = Utils.cmd("echo $SLOT");
|
|
||||||
if (s != null) {
|
|
||||||
options.add(mm.getString(R.string.install_second_slot));
|
|
||||||
}
|
|
||||||
char[] slot = s == null ? null : s.toCharArray();
|
|
||||||
new AlertDialog.Builder(activity)
|
new AlertDialog.Builder(activity)
|
||||||
.setTitle(R.string.select_method)
|
.setTitle(R.string.select_method)
|
||||||
.setItems(
|
.setItems(
|
||||||
options.toArray(new String [0]),
|
options.toArray(new String [0]),
|
||||||
(dialog, idx) -> {
|
(dialog, idx) -> {
|
||||||
String boot;
|
|
||||||
DownloadReceiver receiver = null;
|
DownloadReceiver receiver = null;
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -145,7 +158,7 @@ public class ShowUI {
|
|||||||
activity,
|
activity,
|
||||||
new DownloadReceiver() {
|
new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
Intent intent = new Intent(mm, FlashActivity.class);
|
||||||
intent.setData(uri)
|
intent.setData(uri)
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
@@ -163,47 +176,29 @@ public class ShowUI {
|
|||||||
case 0:
|
case 0:
|
||||||
receiver = new DownloadReceiver() {
|
receiver = new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Utils.showUriSnack(activity, uri);
|
SnackbarMaker.showUri(activity, uri);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
boot = mm.bootBlock;
|
|
||||||
if (boot == null)
|
|
||||||
return;
|
|
||||||
receiver = new DownloadReceiver() {
|
receiver = new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
Intent intent = new Intent(mm, FlashActivity.class);
|
||||||
intent.setData(uri)
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION,
|
||||||
.putExtra(Const.Key.FLASH_SET_BOOT, boot)
|
Const.Value.FLASH_MAGISK);
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
assert (slot != null);
|
|
||||||
// Choose the other slot
|
|
||||||
if (slot[1] == 'a') slot[1] = 'b';
|
|
||||||
else slot[1] = 'a';
|
|
||||||
// Then find the boot image again
|
|
||||||
boot = Utils.cmd(
|
|
||||||
"SLOT=" + String.valueOf(slot) +
|
|
||||||
"; find_boot_image;" +
|
|
||||||
"echo \"$BOOTIMAGE\""
|
|
||||||
);
|
|
||||||
Shell.Async.su("mount_partitions");
|
|
||||||
if (boot == null)
|
|
||||||
return;
|
|
||||||
receiver = new DownloadReceiver() {
|
receiver = new DownloadReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
Intent intent = new Intent(mm, FlashActivity.class);
|
||||||
intent.setData(uri)
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION,
|
||||||
.putExtra(Const.Key.FLASH_SET_BOOT, boot)
|
Const.Value.FLASH_SECOND_SLOT);
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -213,73 +208,66 @@ public class ShowUI {
|
|||||||
}
|
}
|
||||||
).show();
|
).show();
|
||||||
})
|
})
|
||||||
.setNeutralButton(R.string.release_notes, (d, i) -> {
|
.setNegativeButton(R.string.no_thanks, null);
|
||||||
if (mm.releaseNoteLink != null) {
|
if (!TextUtils.isEmpty(mm.magiskNoteLink)) {
|
||||||
Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(mm.releaseNoteLink));
|
b.setNeutralButton(R.string.release_notes, (d, i) -> {
|
||||||
|
if (mm.magiskNoteLink.contains("forum.xda-developers")) {
|
||||||
|
// Open forum links in browser
|
||||||
|
Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(mm.magiskNoteLink));
|
||||||
openLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
openLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
mm.startActivity(openLink);
|
mm.startActivity(openLink);
|
||||||
|
} else {
|
||||||
|
new MarkDownWindow(activity, null, mm.magiskNoteLink).exec();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
}
|
||||||
.show();
|
b.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void managerInstallDialog(Activity activity) {
|
public static void managerInstallDialog(Activity activity) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
||||||
mm.remoteManagerVersionString, mm.remoteManagerVersionCode);
|
mm.remoteManagerVersionString, mm.remoteManagerVersionCode);
|
||||||
new AlertDialogBuilder(activity)
|
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
||||||
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)))
|
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)))
|
||||||
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setPositiveButton(R.string.install, (d, i) -> {
|
.setPositiveButton(R.string.install, (d, i) -> {
|
||||||
Utils.runWithPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
|
com.topjohnwu.magisk.components.Activity.runWithPermission(activity,
|
||||||
|
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
Intent intent = new Intent(mm, ManagerUpdate.class);
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
||||||
mm.sendBroadcast(intent);
|
mm.sendBroadcast(intent);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
.setNegativeButton(R.string.no_thanks, null);
|
||||||
.show();
|
if (!TextUtils.isEmpty(mm.managerNoteLink)) {
|
||||||
|
b.setNeutralButton(R.string.app_changelog, (d, i) ->
|
||||||
|
new MarkDownWindow(activity, null, mm.managerNoteLink).exec());
|
||||||
|
}
|
||||||
|
b.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void uninstallDialog(Activity activity) {
|
public static void uninstallDialog(Activity activity) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
MagiskManager mm = Utils.getMagiskManager(activity);
|
||||||
new AlertDialogBuilder(activity)
|
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
||||||
.setTitle(R.string.uninstall_magisk_title)
|
.setTitle(R.string.uninstall_magisk_title)
|
||||||
.setMessage(R.string.uninstall_magisk_msg)
|
.setMessage(R.string.uninstall_magisk_msg)
|
||||||
.setPositiveButton(R.string.complete_uninstall, (d, i) -> {
|
.setNeutralButton(R.string.restore_img, (d, i) -> new RestoreImages(activity).exec());
|
||||||
ByteArrayOutputStream uninstaller = new ByteArrayOutputStream();
|
if (!TextUtils.isEmpty(mm.uninstallerLink)) {
|
||||||
try (InputStream in = mm.getAssets().open(Const.UNINSTALLER)) {
|
b.setPositiveButton(R.string.complete_uninstall, (d, i) ->
|
||||||
ShellUtils.pump(in, uninstaller);
|
Utils.dlAndReceive(activity, new DownloadReceiver() {
|
||||||
} catch (IOException e) {
|
@Override
|
||||||
e.printStackTrace();
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
return;
|
Intent intent = new Intent(context, FlashActivity.class)
|
||||||
}
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
ByteArrayOutputStream utils = new ByteArrayOutputStream();
|
.setData(uri)
|
||||||
try (InputStream in = mm.getAssets().open(Const.UTIL_FUNCTIONS)) {
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
||||||
ShellUtils.pump(in, utils);
|
context.startActivity(intent);
|
||||||
} catch (IOException e) {
|
}
|
||||||
e.printStackTrace();
|
}, mm.uninstallerLink, "magisk-uninstaller.zip"));
|
||||||
return;
|
}
|
||||||
}
|
b.show();
|
||||||
|
|
||||||
Shell.Sync.su(
|
|
||||||
Utils.fmt("echo '%s' > /cache/%s", uninstaller.toString().replace("'", "'\\''"), Const.UNINSTALLER),
|
|
||||||
Utils.fmt("echo '%s' > %s/%s", utils.toString().replace("'", "'\\''"),
|
|
||||||
mm.magiskVersionCode >= Const.MAGISK_VER.HIDDEN_PATH ? "/data/adb/magisk" : "/data/magisk", Const.UTIL_FUNCTIONS)
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
uninstaller.close();
|
|
||||||
utils.close();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
|
|
||||||
MagiskManager.toast(R.string.uninstall_toast, Toast.LENGTH_LONG);
|
|
||||||
new Handler().postDelayed(() -> Utils.uninstallPkg(mm.getPackageName()), 5000);
|
|
||||||
})
|
|
||||||
.setNeutralButton(R.string.restore_img, (d, i) -> new RestoreImages().exec())
|
|
||||||
.setNegativeButton(R.string.uninstall_app, (d, i) -> Utils.uninstallPkg(mm.getPackageName()))
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,7 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Iterator;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Topic {
|
public class Topic {
|
||||||
@@ -15,23 +14,24 @@ public class Topic {
|
|||||||
private List<WeakReference<Subscriber>> subscribers;
|
private List<WeakReference<Subscriber>> subscribers;
|
||||||
private Object[] results;
|
private Object[] results;
|
||||||
|
|
||||||
public void subscribe(Subscriber sub) {
|
public Topic() {
|
||||||
if (subscribers == null) {
|
subscribers = new SyncArrayList<>();
|
||||||
subscribers = new LinkedList<>();
|
}
|
||||||
}
|
|
||||||
|
public synchronized void subscribe(Subscriber sub) {
|
||||||
subscribers.add(new WeakReference<>(sub));
|
subscribers.add(new WeakReference<>(sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsubscribe() {
|
public synchronized void unsubscribe() {
|
||||||
subscribers = null;
|
subscribers = new SyncArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsubscribe(Subscriber sub) {
|
public synchronized void unsubscribe(Subscriber sub) {
|
||||||
for (Iterator<WeakReference<Subscriber>> i = subscribers.iterator(); i.hasNext();) {
|
List<WeakReference<Subscriber>> subs = subscribers;
|
||||||
WeakReference<Subscriber> subscriber = i.next();
|
subscribers = new ArrayList<>();
|
||||||
if (subscriber.get() == null || subscriber.get() == sub) {
|
for (WeakReference<Subscriber> subscriber : subs) {
|
||||||
i.remove();
|
if (subscriber.get() != null && subscriber.get() != sub)
|
||||||
}
|
subscribers.add(subscriber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,11 +52,11 @@ public class Topic {
|
|||||||
if (record)
|
if (record)
|
||||||
state = PUBLISHED;
|
state = PUBLISHED;
|
||||||
this.results = results;
|
this.results = results;
|
||||||
if (subscribers != null) {
|
// Snapshot
|
||||||
for (WeakReference<Subscriber> subscriber : subscribers) {
|
List<WeakReference<Subscriber>> subs = subscribers;
|
||||||
if (subscriber.get() != null)
|
for (WeakReference<Subscriber> subscriber : subs) {
|
||||||
subscriber.get().onTopicPublished(this);
|
if (subscriber != null && subscriber.get() != null)
|
||||||
}
|
subscriber.get().onTopicPublished(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,4 +89,11 @@ public class Topic {
|
|||||||
void onTopicPublished(Topic topic);
|
void onTopicPublished(Topic topic);
|
||||||
Topic[] getSubscription();
|
Topic[] getSubscription();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class SyncArrayList<E> extends ArrayList<E> {
|
||||||
|
@Override
|
||||||
|
public synchronized boolean add(E e) {
|
||||||
|
return super.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
||||||
import com.topjohnwu.utils.JarMap;
|
import com.topjohnwu.utils.JarMap;
|
||||||
import com.topjohnwu.utils.SignAPK;
|
import com.topjohnwu.utils.SignAPK;
|
||||||
|
|
||||||
@@ -9,6 +11,7 @@ import java.io.BufferedOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
@@ -16,13 +19,13 @@ import java.util.zip.ZipInputStream;
|
|||||||
|
|
||||||
public class ZipUtils {
|
public class ZipUtils {
|
||||||
|
|
||||||
public static void unzip(File zip, File folder, String path, boolean junkPath) throws Exception {
|
public static void unzip(File zip, File folder, String path, boolean junkPath) throws IOException {
|
||||||
InputStream in = new BufferedInputStream(new FileInputStream(zip));
|
InputStream in = new BufferedInputStream(new FileInputStream(zip));
|
||||||
unzip(in, folder, path, junkPath);
|
unzip(in, folder, path, junkPath);
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws Exception {
|
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws IOException {
|
||||||
try {
|
try {
|
||||||
ZipInputStream zipfile = new ZipInputStream(zip);
|
ZipInputStream zipfile = new ZipInputStream(zip);
|
||||||
ZipEntry entry;
|
ZipEntry entry;
|
||||||
@@ -38,12 +41,16 @@ public class ZipUtils {
|
|||||||
name = entry.getName();
|
name = entry.getName();
|
||||||
}
|
}
|
||||||
File dest = new File(folder, name);
|
File dest = new File(folder, name);
|
||||||
dest.getParentFile().mkdirs();
|
if (!dest.getParentFile().exists() && !dest.getParentFile().mkdirs()) {
|
||||||
try (FileOutputStream out = new FileOutputStream(dest)) {
|
dest = new SuFile(folder, name);
|
||||||
|
dest.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
try (OutputStream out = new SuFileOutputStream(dest)) {
|
||||||
ShellUtils.pump(zipfile, out);
|
ShellUtils.pump(zipfile, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
}
|
||||||
|
catch(IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
BIN
src/full/res/drawable-nodpi/logo.png
Normal file
BIN
src/full/res/drawable-nodpi/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
18
src/full/res/drawable/ic_magisk_outline.xml
Normal file
18
src/full/res/drawable/ic_magisk_outline.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<vector android:height="48dp" android:viewportHeight="720"
|
||||||
|
android:viewportWidth="720" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M332.48,421.18c0,0 3.77,22.45 -0.82,71.95c-5.76,62.06 23.64,160.64 23.64,160.64c0,0 40.1,-98.78 33.1,-162.59c-5.75,-52.45 2.6,-70.79 0.82,-68.33c-30.81,42.57 -56.75,-1.67 -56.75,-1.67z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M407.6,474.45c5.01,38.77 -0.57,60.01 -7.81,101.51c-3.66,20.99 74.78,-63.1 104.86,-113.23c5.02,-8.36 -28.77,32.6 -62.19,3.35c-23.18,-20.28 -27.16,-26.44 -45.18,-44.06c-6.08,-5.94 6.74,24.72 10.32,52.43z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M321.99,425.09c-18.02,17.62 -22,23.78 -45.18,44.06c-33.42,29.25 -67.21,-11.71 -62.19,-3.35c30.08,50.13 108.52,134.22 104.86,113.23c-7.24,-41.5 -12.82,-62.74 -7.81,-101.51c3.58,-27.71 16.4,-58.37 10.32,-52.43z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M399.15,355.87c36.67,10.57 50.89,61.5 87.91,67.8c7.65,1.3 16.27,3.6 26.31,3.12c18.77,-0.9 42.51,-11.51 74.22,-56.5c9.38,-13.3 -23.27,85.66 -105.13,86.86c-59.96,0.88 -66.97,-58.7 -106.93,-60.51c-14.43,-0.65 -15.34,-28.17 -15.34,-28.17c0,0 17.22,-18.86 38.96,-12.6z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M321.51,355.59c-36.67,10.57 -50.89,61.5 -87.91,67.8c-7.65,1.3 -16.27,3.6 -26.31,3.12c-18.77,-0.9 -42.51,-11.51 -74.22,-56.5c-9.38,-13.3 23.27,85.66 105.13,86.86c59.96,0.88 66.97,-58.7 106.93,-60.51c14.43,-0.65 15.34,-28.17 15.34,-28.17c0,0 -17.22,-18.86 -38.96,-12.6z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M458.64,355.09c36.87,27.94 25.88,58.7 46.57,49.92c69.7,-29.55 57.51,-181.21 51.87,-162.87c-31.77,103.41 -100.99,109.2 -167.61,61.63c-13.01,-9.29 48.38,35.57 69.16,51.31z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M330.91,303.77c-66.62,47.56 -135.84,41.78 -167.61,-61.63c-5.63,-18.34 -17.82,133.31 51.87,162.87c20.7,8.78 9.7,-21.98 46.57,-49.92c20.78,-15.75 82.17,-60.6 69.16,-51.31z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M465.61,318c80.43,-3.32 95.29,-135.17 88.96,-119.08c-28.39,72.22 -135.86,45.05 -146.13,90.64c-2.02,8.94 18.2,30.06 57.17,28.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M311.95,289.55c-10.27,-45.59 -117.75,-18.41 -146.13,-90.64c-6.32,-16.09 8.53,115.76 88.96,119.08c38.97,1.61 59.19,-19.5 57.17,-28.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M403.42,269.47c0,0 43.73,-23.5 81.16,-33.74c34.99,-9.58 61.22,-33.13 64.14,-58.01c2.18,-18.53 -27.05,-53.55 -27.05,-53.55c0,0 -20.51,56.9 -47.41,85.34c-29.28,30.96 -18.15,26.78 -70.84,59.96z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M246.13,209.51c-26.9,-28.44 -47.41,-85.34 -47.41,-85.34c0,0 -29.23,35.01 -27.05,53.55c2.93,24.88 29.16,48.43 64.14,58.01c37.43,10.25 81.16,33.74 81.16,33.74c-52.69,-33.18 -41.55,-29 -70.84,-59.96z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M398.12,265.85c47.36,-38.85 72.53,-89.54 113.51,-145.02c7.73,-10.46 -34.58,-35.7 -51.31,-37.37c-16.73,-1.67 -30.77,59.79 -32.35,95.94c-1.44,33.01 -36.21,91.68 -29.84,86.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M292.42,179.39c-1.58,-36.15 -15.62,-97.61 -32.35,-95.94c-16.73,1.67 -59.04,26.91 -51.31,37.37c40.98,55.48 66.14,106.17 113.51,145.02c6.37,5.22 -28.4,-53.45 -29.84,-86.45z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M402.86,140.35c3.34,-26.76 15.37,-46.32 39.32,-62.75c-21.17,-7.08 -38.77,-12.83 -47.97,-5.3c-9.2,7.53 -34.2,32.7 -30.85,73.68c3.34,40.98 0.18,194.09 7.43,191.25c3.9,-104.87 37.09,-135 32.07,-196.89z"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M349.59,337.24c7.24,2.83 4.08,-150.27 7.43,-191.25c3.34,-40.98 -21.65,-66.16 -30.85,-73.68c-9.2,-7.53 -26.8,-1.78 -47.97,5.3c23.95,16.43 35.98,35.98 39.32,62.75c-5.02,61.89 28.17,92.02 32.07,196.89z"/>
|
||||||
|
</vector>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user