Compare commits

...

252 Commits

Author SHA1 Message Date
topjohnwu
a54eaf5371 Hardcode snet extension URL and version 2018-12-08 00:43:50 -05:00
topjohnwu
8032bd4bb9 Add v6.1.0 changelog 2018-12-07 23:25:49 -05:00
topjohnwu
ea1beec2f7 Tweak some strings 2018-12-07 22:08:03 -05:00
JoanVC100
05f2f6820e Little correction ca-string 2018-12-07 21:57:30 -05:00
topjohnwu
0f5f15a5ce Stop signing module zips
Nobody should be using the signature verification in recoveries
2018-12-07 21:56:54 -05:00
topjohnwu
14ac37e8a5 Fix and optimize APK installations 2018-12-07 21:42:53 -05:00
topjohnwu
1fae89cbb6 Add new cpio command: "exists", to magiskboot 2018-12-05 20:27:48 -05:00
topjohnwu
8b4008798f Add backward compat paths 2018-12-05 20:10:59 -05:00
topjohnwu
fd4faf59b8 Use tr for replacing whitespace to newline
Close #824
2018-12-05 20:07:15 -05:00
topjohnwu
109891d668 Make apk_install more portable 2018-12-05 18:36:27 -05:00
topjohnwu
bdea796121 Fix strings 2018-12-05 17:39:32 -05:00
Oliver Cervera
c8813c05c9 Italian translation update
Update based on latest commits, mainly from here ee2c801fe0
2018-12-05 12:53:54 -05:00
Pzqqt
1cff08ce5d Fix possible error block counts
Add "-k" parameter to force the unit to 1024 bytes.
2018-12-05 12:53:06 -05:00
topjohnwu
a868118f6f Use defined symbols in SDK 16 libsqlite.so 2018-12-05 12:48:01 -05:00
topjohnwu
e5c62f5750 Allow post-fs-data module scripts to change module state 2018-12-05 12:47:29 -05:00
topjohnwu
4084e8790b Fix APK installation on old Android versions 2018-12-04 20:27:09 -05:00
linar10
8d931dd773 Update strings.xml 2018-12-04 14:35:18 -05:00
dark-basic
25d6366297 Update String Spanish full ver
New aggregate lines and structure changes
2018-12-04 14:34:41 -05:00
topjohnwu
08cd5b81d1 Try to repair boot_hdr v1 entries 2018-12-04 03:30:43 -05:00
Albert I
5d3a8a5b1a Update Indonesian translations
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-12-04 02:15:15 -05:00
topjohnwu
79b84da4b8 Adjust for new FrankeNDK 2018-12-04 02:08:51 -05:00
topjohnwu
68b07c5913 Use flags for smaller binary 2018-12-03 19:43:02 -05:00
topjohnwu
553db9124d Update trad. Chinese translation 2018-12-03 10:14:13 -05:00
topjohnwu
b495f37299 Optimize imports 2018-12-03 10:09:14 -05:00
Rom
1915547594 Update French translation 2018-12-03 10:07:57 -05:00
vvb2060
03de29164a Update zh-rCN translation 2018-12-03 10:06:58 -05:00
topjohnwu
86d8b50547 Update CheckUpdate 2018-12-03 10:05:33 -05:00
topjohnwu
7b04386162 Patch app label when repackaging 2018-12-03 09:52:41 -05:00
topjohnwu
07bfdf3e4d Allow multiple progress notifications 2018-12-03 02:28:20 -05:00
topjohnwu
d510224e2a Use notifications for hiding manager 2018-12-03 02:24:07 -05:00
topjohnwu
e658f9297d Make progress notifications persist 2018-12-03 01:52:36 -05:00
topjohnwu
2b502e9a0f Small reorganization 2018-12-03 01:44:13 -05:00
topjohnwu
59141f9bbe Show failure when download fails 2018-12-02 23:44:56 -05:00
topjohnwu
3af66b72f2 Use notifications when downloading modules 2018-12-02 23:41:16 -05:00
topjohnwu
422c24bd68 Remove debug loggin in GeneralReceiver 2018-12-02 22:52:23 -05:00
topjohnwu
f0f87c8eb9 Reduce BroadcastReceivers 2018-12-02 16:53:00 -05:00
topjohnwu
80dad54119 Some cleanups 2018-12-02 15:28:18 -05:00
topjohnwu
56a76df28e Fix string resources in shortcut 2018-12-02 15:16:05 -05:00
topjohnwu
ee2c801fe0 Better progress notifications 2018-12-02 15:15:42 -05:00
Rom
fc314cc248 French translation update 2018-12-02 12:19:31 -05:00
topjohnwu
fe231a4c80 Rename app name to Manager 2018-12-02 05:36:14 -05:00
Eray Rafet
2e2bbe0a7f A small fix 2018-12-02 05:34:51 -05:00
topjohnwu
857e6e8345 Tweak notifications 2018-12-02 05:33:53 -05:00
topjohnwu
3402981ada Move some string resources 2018-12-02 05:15:16 -05:00
topjohnwu
f401e577e5 Better Proguard optimization 2018-12-02 04:56:13 -05:00
topjohnwu
0241a50c6f Stop using platform provided DownloadManager 2018-12-02 04:47:57 -05:00
topjohnwu
2a2e1236fc Use magic macros 2018-12-01 03:53:58 -05:00
topjohnwu
9b170f2b4f Switch from deprecated AUDITDENY to DONTAUDIT 2018-11-29 06:42:04 -05:00
topjohnwu
51e9ff59de Temporarily suppress warnings when applying Magisk rules 2018-11-29 06:31:05 -05:00
topjohnwu
2977dbcded Remove all dontaudit in magisk rules 2018-11-29 06:28:37 -05:00
topjohnwu
ac60b51035 Support removing redundant avtab nodes 2018-11-29 05:42:08 -05:00
topjohnwu
4c2f33a089 Remove '--install' 2018-11-29 04:35:43 -05:00
topjohnwu
3b071116ac Update magiskpolicy
- Generalize avtab node extraction and insertion
- Add new supported rules: type_change, type_member
- Update help message with official policy language
2018-11-29 03:46:29 -05:00
Oliver Cervera
a9f265a591 Small grammatical changes / values-it 2018-11-28 01:41:05 -05:00
Eray Rafet
5b62fc8103 Update Bulgarian translation 2018-11-28 01:40:50 -05:00
Eray Rafet
0598f5f89a Update Bulgarian translation
Grammar, spelling and punctuation fixes
2018-11-28 01:40:41 -05:00
topjohnwu
f723427b8b Add built-in procfs protection on SDK 24+
More information in the Medium Post:
https://medium.com/@topjohnwu/from-anime-game-to-android-system-security-vulnerability-9b955a182f20
2018-11-28 01:27:32 -05:00
topjohnwu
f69a004c1c Use raw execve
Some devices have broken libc...
2018-11-28 00:07:57 -05:00
topjohnwu
1134b18a8b Rename application label to "Magic" to prevent detection 2018-11-27 03:56:14 -05:00
topjohnwu
2e4aa507f7 Use magisk to clone file attributes 2018-11-27 03:56:14 -05:00
topjohnwu
5fb96cdcf4 Auto launch new app after repackaging/restoring Manager 2018-11-27 03:56:14 -05:00
topjohnwu
e8cba3524e Kill target processes properly 2018-11-27 03:56:14 -05:00
younis12c
7e6b5363f1 Update strings.xml
complete translation added
2018-11-26 20:13:31 -05:00
topjohnwu
29457a1d28 Small adjustments 2018-11-26 03:26:45 -05:00
topjohnwu
731455f164 Update exec functions signatures 2018-11-26 03:06:48 -05:00
topjohnwu
b01a8cace6 Always try native accept4 2018-11-26 02:57:34 -05:00
vvb2060
72db5b4fac Update zh-rCN translation 2018-11-25 17:04:45 -05:00
topjohnwu
ddfd42994e Module id and name can no longer be null
Close #797
2018-11-25 17:04:23 -05:00
topjohnwu
2a9ff9c5ef Update dependencies 2018-11-25 03:33:41 -05:00
Ilya Kushnir
6d49f05356 Minor fixes to RU strings 2018-11-24 15:53:42 -05:00
Albert I
85a5e62e36 Update Indonesian translations
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-11-24 15:53:35 -05:00
topjohnwu
e67965a381 Silent some errors 2018-11-24 15:53:15 -05:00
topjohnwu
ec4723096f Prevent file descriptor from unclosed 2018-11-23 21:15:44 -05:00
topjohnwu
762b678d24 Prevent any SELinux issues of root shell streams 2018-11-23 21:08:06 -05:00
topjohnwu
38fcc57bbf Use component name as targets
Services can name their process name arbitrarily, for instance the service in
com.google.android.gms that is responsible for SafetyNet is named
com.google.android.gms.unstable. There are many apps out in the wild use
dedicated services with special names to detect root, and previously the user
is expected to add all of them to the hide list.

In this commit, we change from targeting process names to component names.
On Android, component names are composed of <pkg>/<cls>. When targeting
component names, we can always know what application spawned the new process.
This means that if the user adds a package name to the hidelist, MagiskHide can
now target ALL possible processes of that specific application.

To abide with this change, the default SafetyNet target is now changed from
com.google.android.gms.unstable (process name) to
com.google.android.gms/.droidguard.DroidGuardService (component name)
2018-11-23 15:47:49 -05:00
topjohnwu
c8c57c74cc Optimize proc_monitor 2018-11-23 14:32:33 -05:00
topjohnwu
0784448c69 Remove /.backup folder on start 2018-11-20 05:24:40 -05:00
topjohnwu
de0064af47 Fix SIGWINCH never followed
Close #786
2018-11-20 04:40:42 -05:00
topjohnwu
baae1fc84f Modernize selinux stub 2018-11-20 03:49:44 -05:00
topjohnwu
2ab999f4ca Fix bug in DB query wrapper 2018-11-20 02:20:49 -05:00
topjohnwu
c9f390d6e0 Abort upon any error occurred 2018-11-20 02:20:49 -05:00
Igor Sorocean
af05922ecc Update romanian strings 2018-11-20 02:19:52 -05:00
Nguyễn Trung Hậu
299edbf3ab Updated Vietnamese translations 2018-11-20 02:19:42 -05:00
Rom
c8abed9d48 French translation update 2018-11-20 02:19:30 -05:00
topjohnwu
3622c49ce1 Update busybox 2018-11-18 15:58:41 -05:00
topjohnwu
0462e9a7d9 Update external dependencies 2018-11-18 03:34:59 -05:00
topjohnwu
c3a6091908 Update to 1.29.3 2018-11-18 02:45:21 -05:00
topjohnwu
ab5fedda0b Prevent Magisk database race condition
The database should only be accessed by a single process, which is magiskd.
This means 'magisk --sqlite [SQL]' has to be updated to pass the SQL command to the daemon.
In addition, open the database connection with SQLITE_OPEN_FULLMUTEX to support multithread in magiskd.
2018-11-16 03:20:30 -05:00
topjohnwu
ba70269398 Directly print output over socket 2018-11-16 01:49:15 -05:00
topjohnwu
77fd5fa7de Do not follow symlink when checking legacy paths 2018-11-16 01:16:25 -05:00
topjohnwu
ab74290fe3 Move magiskhide config into database 2018-11-16 01:15:34 -05:00
topjohnwu
3aad9d8166 Add CLI to detect MagiskHide status 2018-11-16 00:37:41 -05:00
topjohnwu
572e078d87 Fully deprecate <mount_point>/.core folder
Symlinks are preserved for backwards compatibility
2018-11-15 22:55:28 -05:00
topjohnwu
ee4548230b Disable native systemless hosts, add built-in systemless hosts module 2018-11-15 13:57:41 -05:00
topjohnwu
96b93bd876 Add function to find manager APK
Close #673
2018-11-15 03:12:31 -05:00
marciozomb13
927f69fe30 Brazilian Portuguese Update 2018-11-15 03:03:21 -05:00
Ian Macdonald
7e9ad5927a Fix grammatical errors, unnatural-sounding English and bad punctuation 2018-11-15 03:03:06 -05:00
Ian Macdonald
6d6b07865e Add 15 and 45 second Request Timeout options. 2018-11-15 03:02:45 -05:00
topjohnwu
376e7977f0 Deprecate path /sbin/.core, switch to /sbin/.magisk
Symlink is preserved for backwards compatibility
2018-11-15 01:36:03 -05:00
topjohnwu
83ae66daea Change stock boot image SHA1 backup method 2018-11-15 00:33:20 -05:00
topjohnwu
89e0be0099 Fix a bug causing magiskhide CLI freezing 2018-11-13 02:22:55 -05:00
topjohnwu
ef40c1212e Prevent infinite loop if process is killed
Close #761
2018-11-13 02:11:02 -05:00
topjohnwu
3a2a2a4ffa Micro optimizations 2018-11-13 02:07:02 -05:00
topjohnwu
9592a69986 Prevent unmounting non-custom mount points 2018-11-13 01:53:48 -05:00
topjohnwu
89be07e1f2 Update to libsu 2.0.3 2018-11-13 00:21:42 -05:00
topjohnwu
c61c3ae0e9 Fix su shell environment setup 2018-11-10 02:17:13 -05:00
topjohnwu
817350c8c5 Update AndroidX 2018-11-09 22:04:04 -05:00
topjohnwu
3603b7c82b Move cmdline and extra_cmdline to the same line 2018-11-08 20:57:30 -05:00
topjohnwu
5743c72cca Minor cleanup 2018-11-08 15:23:36 -05:00
topjohnwu
4cdd66ceff Fix lowmemorykiller crash hell in Pixel 3 2018-11-08 13:41:03 -05:00
topjohnwu
d3947d2cfa Adjust logging in magiskpolicy 2018-11-08 06:43:11 -05:00
topjohnwu
07718b994a Fix magiskinit
The behavior of C and C++ is slightly different, and causes unable to set excl_list
2018-11-08 06:07:52 -05:00
topjohnwu
ef9d463bd7 Fix PLOGE 2018-11-08 06:07:02 -05:00
topjohnwu
8745c7884e Rename Array to Vector
Finally get rid of the C style vector, rename the template class to its proper name
2018-11-08 05:03:59 -05:00
topjohnwu
b6965105b7 Better parsing logic 2018-11-08 04:57:16 -05:00
topjohnwu
3d269fe8be Migrate MagiskInit to C++ 2018-11-08 04:20:16 -05:00
topjohnwu
be5f00aa1a Prevent stack overflow when managing hide list 2018-11-07 22:46:56 -05:00
topjohnwu
59ba350f34 Fix copy and move assigments of Array 2018-11-07 04:09:37 -05:00
topjohnwu
803c5377a6 Clean init.c 2018-11-07 02:21:15 -05:00
topjohnwu
7c12bf7fa1 Modernize code base 2018-11-07 02:10:38 -05:00
topjohnwu
ca35a9681f Minor code improvements 2018-11-06 05:02:30 -05:00
topjohnwu
9fe5f37337 Minor code improvements 2018-11-05 14:37:47 -05:00
topjohnwu
0742901cd2 Modernize database code 2018-11-04 18:24:08 -05:00
topjohnwu
5e4d2dedbe Minor log_daemon changes 2018-11-04 17:23:08 -05:00
topjohnwu
411ea56a3e Add personal update script to gitignore 2018-11-04 04:16:11 -05:00
topjohnwu
cda57dd4b4 Fully migrate Magisk to C++ 2018-11-04 04:15:51 -05:00
topjohnwu
4351de503f Migrate exec function to C++ arrays 2018-11-03 04:03:11 -04:00
topjohnwu
6339ba6bfb Upgrade libutils to C++ 2018-11-03 03:06:01 -04:00
topjohnwu
ef6677f43d Source reorganization 2018-11-03 00:26:04 -04:00
topjohnwu
a7824af5a8 Expose persist prop API 2018-11-03 00:15:21 -04:00
vvb2060
1eb7d7b7a8 Add FLAG_INCLUDE_STOPPED_PACKAGES for broadcast 2018-11-03 00:04:27 -04:00
topjohnwu
11c33d4447 Migrate resetprop to C++ 2018-11-02 23:56:15 -04:00
topjohnwu
b8a3cc8b60 Separate magiskhide logic from main daemon 2018-11-01 14:08:33 -04:00
topjohnwu
27c688252d Store hidelist in magisk database 2018-11-01 13:23:12 -04:00
topjohnwu
3e2afd4b1d Better debugging output 2018-11-01 01:16:15 -04:00
topjohnwu
f45b0686d2 Mount ext4 images with noatime flag 2018-10-29 21:44:22 -04:00
vvb2060
1f3f881f81 Skip files when scanning modules 2018-10-28 17:21:58 -04:00
topjohnwu
ceb51bb14f daemon.c uses external flags 2018-10-28 16:55:51 -04:00
topjohnwu
3e22573d8d Upgrade snet extension 2018-10-28 16:55:51 -04:00
topjohnwu
79418a3767 Upgrade Bouncycastle 2018-10-28 16:55:51 -04:00
Shahmi Saidi
40d4683de1 Hint what FBE means in details.md 2018-10-28 15:15:19 -04:00
topjohnwu
79e5b54ec7 Remove redundant semicolon 2018-10-28 15:13:30 -04:00
topjohnwu
bd81923f2f Revert "Make dark theme cards slightly darker"
This reverts commit 675d6d8328.
2018-10-28 14:59:45 -04:00
topjohnwu
69560b8ad7 Fix and prevent crashes 2018-10-28 14:54:07 -04:00
topjohnwu
dc413e7b73 Retry db construction if first time failed 2018-10-28 14:49:04 -04:00
topjohnwu
7fc00c446b Buffer OutputStream to prevent broken pipe error 2018-10-28 05:25:33 -04:00
topjohnwu
2efc423cf8 Add missing flags and move debug logging logic to libutils 2018-10-28 04:25:31 -04:00
topjohnwu
8ec3086cdd Make sure magisklogd is properly initialized 2018-10-28 04:24:53 -04:00
topjohnwu
5fc7079023 Sort Policies before returning 2018-10-28 03:00:49 -04:00
topjohnwu
bfbd254be7 Update donation link 2018-10-28 02:48:01 -04:00
topjohnwu
f8ea43466c Only allow device owner to hide/restore Magisk Manager 2018-10-28 00:58:22 -04:00
topjohnwu
75ab1fa570 Micro optimizations 2018-10-28 00:54:56 -04:00
topjohnwu
bf4a46d57c Optimize logging in Magisk Manager 2018-10-27 22:06:24 -04:00
topjohnwu
1046dd5eda Default to cmdline logging 2018-10-27 18:34:38 -04:00
topjohnwu
f9e32a119a Fix bug when query database with specific keys 2018-10-27 17:56:20 -04:00
topjohnwu
dbb8b8a439 Handle magisk.db completely natively
Prevent database corruption due to different Android application sqlite default settings
2018-10-27 17:54:48 -04:00
topjohnwu
2a65c3dc8f Prepare for new database implementation 2018-10-27 17:38:23 -04:00
topjohnwu
f17ec9e9d7 Update sqlite header 2018-10-27 03:30:20 -04:00
Nicholas
675d6d8328 Make dark theme cards slightly darker
Use #323232 instead of #424242

Of course this is just a suggestion, use other codes if you wish. I just find the current color a bit too light for a dark theme.
2018-10-26 17:09:56 -04:00
topjohnwu
6dc9ccad75 Use const char* 2018-10-26 17:02:56 -04:00
topjohnwu
6add02702b Fix bug in MagiskBoot 2018-10-26 17:02:07 -04:00
topjohnwu
958d6377e3 Improve XML string matching code 2018-10-26 02:50:45 -04:00
topjohnwu
9954154ca2 Move functions out of libutils 2018-10-24 22:23:14 -04:00
topjohnwu
4ecbf8c12c Remove recovery_dtbo when cleanup 2018-10-24 22:23:14 -04:00
topjohnwu
fc8a3c5fb4 Migrate MagiskBoot to C++ 2018-10-24 22:23:14 -04:00
vvb2060
01e7dff1a0 Fix crash when using other su 2018-10-24 04:59:29 -04:00
topjohnwu
018c0064cd Make sure boot_img is initialized correctly 2018-10-22 01:58:50 -04:00
topjohnwu
c2b016370b Make a copy of logcat and use that instead
When Magisk is magic mounting /system/bin, there is a chance that logcat would be temporarily unavailable. Leave a copy and use that for magisklogd
2018-10-20 21:46:12 -04:00
daveyannihilation
fc791b4371 Fix Dark theme to display cards as slightly lighter than background as per Material Design standards. Also redirect colors to app as opposed to calling on framework 2018-10-20 21:13:13 -04:00
topjohnwu
f76bb009f4 Update changelogs 2018-10-20 20:11:09 -04:00
topjohnwu
8a1292b295 Ask permissions to read internal storage 2018-10-20 19:42:46 -04:00
topjohnwu
d7d80d3fc1 Update encryption detection for determining default flags 2018-10-20 17:10:35 -04:00
topjohnwu
41b01003fd Always ACK before doing anything 2018-10-20 16:12:08 -04:00
topjohnwu
6557070ae1 Try to flush database before uninstalling 2018-10-20 15:31:41 -04:00
topjohnwu
e7e580e177 Remove support for Magisk lower than 1500 2018-10-20 15:04:15 -04:00
topjohnwu
dd9ddd2019 Remove unnecessary instruction from Defex hexpatch
Close #489
2018-10-20 00:28:09 -04:00
topjohnwu
74aae523ba Properly support boot image header v1
Close #695
2018-10-20 00:27:56 -04:00
topjohnwu
48c40f9516 Prevent Resources
Fix #619
2018-10-17 19:44:48 -04:00
topjohnwu
e0e7674715 Fix close button in FlashActivity 2018-10-17 15:36:09 -04:00
topjohnwu
e1a65276b9 Switch to general Samsung defex patch
Should be future proof unless code changes
2018-10-17 03:17:24 -04:00
muhammeteminturgut
469adc85ad Update Turkish translations 2018-10-16 21:11:43 -04:00
vvb2060
e1b181ca4e Hide system in MagiskHide list 2018-10-16 21:09:18 -04:00
topjohnwu
a4f0fbf8b7 Switch to butterknife
Finally support AndroidX and obfuscation
2018-10-16 21:00:01 -04:00
topjohnwu
190cdaddf8 Update README 2018-10-16 02:06:07 -04:00
topjohnwu
5c4ba13839 Add installation details 2018-10-16 01:55:28 -04:00
topjohnwu
e62630cf3e Add MagiskHide tutorials 2018-10-15 18:04:51 -04:00
topjohnwu
36fe7846c0 Update documentations
Still WIP
2018-10-15 04:33:42 -04:00
topjohnwu
8d150dd67a Update documentation
Still WIP
2018-10-15 00:46:37 -04:00
topjohnwu
506df00d81 Upgrade AGP 2018-10-12 21:51:58 -04:00
topjohnwu
a9121fa28f Reorganize libutils and cleanups 2018-10-12 21:46:09 -04:00
topjohnwu
d5a56d9e85 Fix bootloop for some devices with two /data
Close #654
2018-10-12 00:54:55 -04:00
topjohnwu
acf7c0c665 Minor reorganization of daemons 2018-10-12 00:50:47 -04:00
topjohnwu
619d48c97a Remove doc changelogs 2018-10-05 17:55:06 -04:00
topjohnwu
2cb198c38c Update README 2018-10-05 17:52:40 -04:00
topjohnwu
e8e39e0f3c Use poll instead of select
Close #637
2018-10-04 15:06:13 -04:00
topjohnwu
37860181d4 Finish su implementation 2018-10-04 14:41:48 -04:00
topjohnwu
d119dd9a0c Rewrite su daemon and client 2018-10-04 04:59:51 -04:00
topjohnwu
09ef19f7ec Code cleanups 2018-10-04 01:49:52 -04:00
topjohnwu
6a06c92fa6 Simplify su_info caches
No more lists. 99.999% it will only handle a single excessive requestor anyways.
2018-10-03 23:31:15 -04:00
topjohnwu
58ae596b0f Require fp auth when toggling su permission if required
Close #656
2018-09-29 02:21:14 -04:00
topjohnwu
f1ca21678d Set boolean when toggling 2018-09-29 02:02:41 -04:00
topjohnwu
d7eeef2c8a Separate fingerprint authentication dialog code 2018-09-29 01:57:51 -04:00
topjohnwu
4f626897f2 Cleanup 2018-09-29 00:28:12 -04:00
topjohnwu
b127e01845 Simplify debug flag propagation 2018-09-28 02:05:55 -04:00
topjohnwu
2118beeb23 Magisk-Modules-Repo now names repo with ID, simplify logic here 2018-09-28 01:58:28 -04:00
topjohnwu
5020cd1bbf Small cleanup 2018-09-28 01:25:43 -04:00
topjohnwu
cce636224c Reorganization 2018-09-27 18:26:41 -04:00
topjohnwu
60b3b8ddce Better incremental builds 2018-09-27 03:56:56 -04:00
topjohnwu
41446ec9ba Separate libutils and libsystemproperties 2018-09-27 03:30:16 -04:00
topjohnwu
df8b047bca Generalize logging interface 2018-09-27 03:11:10 -04:00
topjohnwu
12ced52012 Remove unused flag 2018-09-27 00:30:10 -04:00
topjohnwu
1d53335ae5 Dynamic load libselinux 2018-09-27 00:09:59 -04:00
topjohnwu
971a50d290 Update to Android Studio 3.2 2018-09-25 00:39:49 -04:00
topjohnwu
36dd9106a8 Stable AndroidX 2018-09-21 21:46:09 -04:00
John Wu
0a4ee3ffc7 Update README.MD 2018-09-21 12:01:59 -04:00
topjohnwu
cfe32f1a70 Update Magisk Manager changelogs 2018-09-20 22:34:09 -04:00
Taras
d877f5d5c6 update Ukrainian strings 2018-09-20 16:56:17 -04:00
yuchenlin
0ab6ffefb4 utils/misc.c: prevent file staying opened when function leaving
The utils function may be called in any situation, such as in daemon. We
should guarantee that all the resource got from this function released
normally.

Signed-off-by: yuchenlin <npes87184@gmail.com>
2018-09-20 16:55:48 -04:00
topjohnwu
a292a1d23a Cleanup and add new rules
Close #607
2018-09-20 16:55:16 -04:00
topjohnwu
3f87f6aee3 Fix output in Magisk Manager if no root exists 2018-09-20 16:21:22 -04:00
topjohnwu
04bcd145d3 Add a.a alias to BootSigner 2018-09-20 15:37:59 -04:00
topjohnwu
244e811291 Remove icon padding in preference screen
Courtesy of https://stackoverflow.com/a/51568782
2018-09-19 00:06:14 -04:00
topjohnwu
ac7467fb59 Optimize boot signing to use as little memory as possible 2018-09-18 23:48:21 -04:00
topjohnwu
2c0436216f Prevent null strings in modules/repos
Close #620, close #621
2018-09-18 10:04:12 -04:00
topjohnwu
017fbf267b Fix small theme config issue 2018-09-17 23:29:38 -04:00
topjohnwu
e6afbf2ec0 Force remove busybox from APK 2018-09-17 23:03:37 -04:00
topjohnwu
906b4aad9e New method of communication
Introduce a new communication method between Magisk and Magisk Manager.

Magisk used to hardcode classnames and send broadcast/start activities to
specific components. This new method makes no assumption of any class names,
so Magisk Manager can easily be fully obfuscated.

In addition, the new method connects Magisk and Magisk Manager with random
abstract Linux sockets instead of socket files in filesystems, bypassing
file system complexities (selinux, permissions and such)
2018-09-16 04:16:18 -04:00
topjohnwu
4cf8d41f6a Fix FlashActivity crash 2018-09-16 00:18:34 -04:00
topjohnwu
47c860142e Use ClassMaps to prevent errors 2018-09-16 00:08:13 -04:00
topjohnwu
2fba3f213b Use proper socket address length 2018-09-15 02:49:19 -04:00
topjohnwu
af7c6f9fce Fix FlashActivity crash 2018-09-15 01:45:10 -04:00
topjohnwu
78534deab6 Excessive obfuscation when building in release mode
Close #606
2018-09-14 23:00:39 -04:00
topjohnwu
6710314832 Allow all context to SIGCLD magisk
Fix #596
2018-09-11 11:04:51 -04:00
topjohnwu
0cd4fa6fa0 Simplify SignAPK code 2018-09-11 10:48:36 -04:00
topjohnwu
065949496e Migrate to AndroidX support library 2018-09-10 02:27:45 -04:00
topjohnwu
39c82576ae Prevent warning
Close #524
2018-09-09 12:36:42 -04:00
John Wu
37221a508d Update README.MD 2018-09-09 12:25:22 -04:00
John Wu
6b43a32a10 Delete repo_description.png 2018-09-09 10:41:02 -04:00
ImgBotApp
d7cd1ff142 [ImgBot] optimizes images
*Total -- 990.86kb -> 771.48kb (22.14%)

/docs/images/flashfire.png -- 223.55kb -> 153.39kb (31.39%)
/docs/images/repo_description.png -- 38.55kb -> 27.31kb (29.15%)
/docs/images/manager_reboot.png -- 147.43kb -> 111.50kb (24.37%)
/docs/images/install_inactive_slot.png -- 123.15kb -> 99.79kb (18.97%)
/docs/images/restore_img.png -- 134.17kb -> 109.22kb (18.59%)
/docs/images/disable_auto_ota.png -- 160.04kb -> 132.05kb (17.49%)
/docs/images/ota_done.png -- 163.97kb -> 138.22kb (15.71%)
2018-09-09 10:40:40 -04:00
Sandro Jäckel
659d947863 Updated German translations 2018-09-09 10:40:33 -04:00
Rom
39be7a6288 Update stub French translation 2018-09-09 00:38:47 -04:00
Rom
8ac976c579 Update French translation (#576) 2018-09-09 00:38:33 -04:00
Vladimír Kubala
70fd432c57 Update Slovak translation 2018-09-09 00:38:25 -04:00
Jonas Schubert
00135f2f49 updated full\res\values.de\strings.xml 2018-09-09 00:38:11 -04:00
Albert I
9b944bc29c Update Indonesian translations
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-09-09 00:38:02 -04:00
topjohnwu
d520b3d2a0 Request storage permission when patching boot images 2018-09-08 23:27:19 -04:00
topjohnwu
6f41d9855b Randomize service names
Fix Aniplex Game detections.
Close #502, close #513
2018-09-08 23:17:00 -04:00
topjohnwu
2d7c1da741 Better support for external config file 2018-09-06 14:25:35 -04:00
topjohnwu
c0f45b6b1e Add resetprop magic 2018-09-06 02:57:02 -04:00
topjohnwu
7a0025673c Use libsystemproperties in resetprop
Upstream to latest Android Pie
2018-09-06 02:57:02 -04:00
topjohnwu
ad7ec79903 Support custom config paths 2018-09-05 14:24:28 -04:00
topjohnwu
0543239cca Do not merge binaries if fake symlink 2018-09-01 11:15:05 -04:00
350 changed files with 15954 additions and 12200 deletions

4
.gitignore vendored
View File

@@ -3,9 +3,7 @@ out
*.jks
*.apk
config.prop
# Manually dumped jars
snet/libs
update.sh
# Built binaries
native/out

View File

@@ -1,7 +1,15 @@
# Magisk
[Downloads](https://github.com/topjohnwu/Magisk/releases) | [Documentation](https://topjohnwu.github.io/Magisk/) | [XDA Thread](https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445)
## Introduction
Magisk is a suite of open source tools for customizing Android, supporting devices higher than Android 5.0 (API 21). It covers the fundamental parts for Android customization: root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt removals etc.
Furthermore, Magisk provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact. With its systemless nature along with several other hacks, Magisk can hide modifications from nearly any system integrity verifications used in banking apps, corporation monitoring apps, game cheat detections, and most importantly [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html).
## Bug Reports
**Make sure to install the latest [Canary Build](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337) before reporting any bugs!** **DO NOT** report bugs that is already fixed upstream. Follow the instructions in the [Canary Channel XDA Thread](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337), and report a bug either by opening an issue on GitHub or directly in the thread.
## Building Environment Requirements
1. Python 3.5+: run `build.py` script
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
@@ -9,20 +17,18 @@
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
## Building Notes and Instructions
1. Building is supported on macOS, Linux, and Windows using the custom NDK: [FrankeNDK](https://github.com/topjohnwu/FrankeNDK).
2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
3. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
4. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
## Documentation
[Link to Documentation](docs/README.MD)
1. Clone sources with submodules: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
2. Building is supported on macOS, Linux, and Windows. Official releases are built and tested with [FrankeNDK](https://github.com/topjohnwu/FrankeNDK); point `ANDROID_NDK_HOME` to FrankeNDK if you want to use it for compiling.
3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
4. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
5. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
## License
```
Magisk, including all git submodules are free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,

View File

@@ -1,7 +1,8 @@
apply plugin: 'com.android.application'
def configProps = new Properties()
configProps.load(new FileInputStream(rootProject.file('config.prop')))
def configPath = project.hasProperty('configPath') ? project.configPath : rootProject.file('config.prop')
configProps.load(new FileInputStream(configPath))
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -69,15 +70,23 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.core:core:1.0.1'
fullImplementation project(':utils')
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
fullImplementation 'com.github.topjohnwu:libsu:2.0.1'
fullImplementation 'com.amitshekhar.android:android-networking:1.0.2'
fullImplementation 'androidx.appcompat:appcompat:1.0.2'
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.cardview:cardview:${rootProject.ext.androidXVersion}"
fullImplementation "com.google.android.material:material:${rootProject.ext.androidXVersion}"
fullImplementation 'com.github.topjohnwu:libsu:2.1.2'
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
fullImplementation 'org.kamranzafar:jtar:2.3'
fullImplementation 'com.jakewharton:butterknife:8.8.1'
fullAnnotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
def butterKnifeVersion = '9.0.0-rc2'
if (properties.containsKey('android.injected.invoked.from.ide')) {
fullImplementation "com.jakewharton:butterknife-reflect:${butterKnifeVersion}"
} else {
fullImplementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}"
fullAnnotationProcessor "com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
}
}

View File

@@ -16,19 +16,24 @@
# public *;
#}
# Don't obfuscate, we are open source anyway :)
-dontobfuscate
# BouncyCastle
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
-keep,allowoptimization class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
-dontwarn javax.naming.**
# Gson
-keepattributes Signature
# Snet extention
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
# Fast Android Networking Library
-dontwarn okhttp3.**
# Strip logging
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
public *** debug(...);
}
# Excessive obfuscation
-repackageclasses 'a'
-allowaccessmodification
-optimizationpasses 6

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.VIBRATE" />
@@ -8,15 +9,18 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".MagiskManager"
android:theme="@style/AppTheme">
android:name="a.q"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<!-- Activities -->
<activity
android:name=".MainActivity"
android:name="a.b"
android:configChanges="orientation|screenSize"
android:exported="true" />
<activity
android:name=".SplashActivity"
android:name="a.c"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/SplashTheme">
@@ -26,53 +30,64 @@
</intent-filter>
</activity>
<activity
android:name=".AboutActivity"
android:name="a.d"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".DonationActivity"
android:name="a.e"
android:theme="@style/AppTheme.StatusBar"/>
<activity
android:name=".FlashActivity"
android:name="a.f"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".NoUIActivity"
android:name="a.g"
android:theme="@style/AppTheme.Translucent" />
<!-- Superuser -->
<activity
android:name=".superuser.RequestActivity"
android:name="a.m"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
android:theme="@style/SuRequest" />
<activity
android:name=".superuser.RequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
android:theme="@style/AppTheme.Translucent" />
<receiver android:name=".superuser.SuReceiver" />
<receiver android:name=".receivers.BootReceiver">
<!-- Receiver -->
<receiver android:name="a.h">
<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">
<receiver android:name="a.i">
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
<!-- Service -->
<service
android:name=".services.OnBootService"
android:name="a.j"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".services.UpdateCheckService"
android:name="a.k"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />

View File

@@ -0,0 +1,10 @@
package a;
import com.topjohnwu.magisk.utils.BootSigner;
import androidx.annotation.Keep;
@Keep
public class a extends BootSigner {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.MainActivity;
public class b extends MainActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.SplashActivity;
public class c extends SplashActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.AboutActivity;
public class d extends AboutActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.DonationActivity;
public class e extends DonationActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.FlashActivity;
public class f extends FlashActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.NoUIActivity;
public class g extends NoUIActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
public class h extends GeneralReceiver {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
public class i extends ShortcutReceiver {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.services.OnBootService;
public class j extends OnBootService {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.services.UpdateCheckService;
public class k extends UpdateCheckService {
/* stub */
}

View File

@@ -0,0 +1,22 @@
package a;
import android.content.Context;
import android.util.AttributeSet;
import com.topjohnwu.magisk.components.AboutCardRow;
public class l extends AboutCardRow {
/* stub */
public l(Context context) {
super(context);
}
public l(Context context, AttributeSet attrs) {
super(context, attrs);
}
public l(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.SuRequestActivity;
public class m extends SuRequestActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.MagiskManager;
public class q extends MagiskManager {
/* stub */
}

View File

@@ -2,9 +2,6 @@ package com.topjohnwu.magisk;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
@@ -15,8 +12,10 @@ import com.topjohnwu.magisk.utils.Utils;
import java.util.Locale;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AboutActivity extends BaseActivity {
@@ -37,7 +36,7 @@ public class AboutActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
ButterKnife.bind(this);
new AboutActivity_ViewBinding(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -1,5 +1,6 @@
package com.topjohnwu.magisk;
import android.os.Environment;
import android.os.Process;
import java.io.File;
@@ -9,7 +10,6 @@ import java.util.List;
public class Const {
public static final String DEBUG_TAG = "MagiskManager";
public static final String ORIG_PKG_NAME = BuildConfig.APPLICATION_ID;
public static final String MAGISKHIDE_PROP = "persist.magisk.hide";
// APK content
@@ -18,30 +18,28 @@ public class Const {
public static final String SU_KEYSTORE_KEY = "su_key";
// Paths
public static File MAGISK_PATH;
public static final String MAGISK_PATH = "/sbin/.magisk/img";
public static final File EXTERNAL_PATH;
public static File MAGISK_DISABLE_FILE;
public static File MAGISK_HOST_FILE;
static {
/* Prevent crashing on unrooted devices */
MAGISK_PATH = MAGISK_DISABLE_FILE = MAGISK_HOST_FILE = new File("xxx");
MAGISK_DISABLE_FILE = new File("xxx");
EXTERNAL_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
EXTERNAL_PATH.mkdirs();
}
public static final String BUSYBOX_PATH = "/sbin/.core/busybox";
public static final String BUSYBOX_PATH = "/sbin/.magisk/busybox";
public static final String TMP_FOLDER_PATH = "/dev/tmp";
public static final String MAGISK_LOG = "/cache/magisk.log";
public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
// Versions
public static final int UPDATE_SERVICE_VER = 1;
public static int MIN_MODULE_VER() {
return Data.magiskVersionCode >= MAGISK_VER.REMOVE_LEGACY_LINK ? 1500 : 1400;
}
public static final int MIN_MODULE_VER = 1500;
public static final int SNET_EXT_VER = 12;
/* A list of apps that should not be shown as hide-able */
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
"android",
Data.MM().getPackageName(),
"com.google.android.gms"
);
@@ -49,15 +47,11 @@ public class Const {
public static final int USER_ID = Process.myUid() / 100000;
public static final class MAGISK_VER {
public static final int UNIFIED = 1300;
public static final int FBE_AWARE = 1410;
public static final int RESETPROP_PERSIST = 1436;
public static final int MANAGER_HIDE = 1440;
public static final int HIDDEN_PATH = 1460;
public static final int REMOVE_LEGACY_LINK = 1630;
public static final int SEPOL_REFACTOR = 1640;
public static final int FIX_ENV = 1650;
public static final int DBVER_SIX = 17000;
public static final int CMDLINE_DB = 17305;
public static final int HIDE_STATUS = 17315;
}
public static class ID {
@@ -70,20 +64,23 @@ public class Const {
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
public static final int APK_UPDATE_NOTIFICATION_ID = 5;
public static final int DTBO_NOTIFICATION_ID = 7;
public static final String NOTIFICATION_CHANNEL = "magisk_notification";
public static final int HIDE_MANAGER_NOTIFICATION_ID = 8;
public static final String UPDATE_NOTIFICATION_CHANNEL = "update";
public static final String PROGRESS_NOTIFICATION_CHANNEL = "progress";
}
public static class Url {
public static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/stable.json";
public static final String BETA_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/beta.json";
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed&page=%d";
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed";
public static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
public static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
public static final String PAYPAL_URL = "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=CC7FZ7526MNGG";
public static final String PAYPAL_URL = "https://www.paypal.me/topjohnwu";
public static final String PATREON_URL = "https://www.patreon.com/topjohnwu";
public static final String TWITTER_URL = "https://twitter.com/topjohnwu";
public static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
public static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk";
public static final String SNET_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/b66b1a914978e5f4c4bbfd74a59f4ad371bac107/snet.apk";
}
@@ -101,10 +98,12 @@ public class Const {
// intents
public static final String OPEN_SECTION = "section";
public static final String INTENT_SET_FILENAME = "filename";
public static final String INTENT_SET_NAME = "filename";
public static final String INTENT_SET_LINK = "link";
public static final String FLASH_ACTION = "action";
public static final String FLASH_SET_BOOT = "boot";
public static final String BROADCAST_MANAGER_UPDATE = "manager_update";
public static final String BROADCAST_REBOOT = "reboot";
// others
public static final String CHECK_UPDATES = "check_update";
@@ -141,9 +140,6 @@ public class Const {
public static final int NAMESPACE_MODE_ISOLATE = 2;
public static final int NO_NOTIFICATION = 0;
public static final int NOTIFICATION_TOAST = 1;
public static final int NOTIFY_NORMAL_LOG = 0;
public static final int NOTIFY_USER_TOASTS = 1;
public static final int NOTIFY_USER_TO_OWNER = 2;
public static final int SU_PROMPT = 0;
public static final int SU_AUTO_DENY = 1;
public static final int SU_AUTO_ALLOW = 2;

View File

@@ -5,7 +5,11 @@ import android.os.Handler;
import android.os.Looper;
import android.util.Xml;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@@ -18,11 +22,14 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class Data {
// Global app instance
public static WeakReference<MagiskManager> weakApp;
public static Handler mainHandler = new Handler(Looper.getMainLooper());
public static Map<Class, Class> classMap = new HashMap<>();
// Current status
public static String magiskVersionString;
@@ -40,8 +47,6 @@ public class Data {
public static String managerLink;
public static String managerNoteLink;
public static String uninstallerLink;
public static int snetVersionCode;
public static String snetLink;
// Install flags
public static boolean keepVerity = false;
@@ -51,22 +56,39 @@ public class Data {
public static boolean isDarkTheme;
public static int suRequestTimeout;
public static int suLogTimeout = 14;
public static int suAccessState;
public static boolean suFingerprint;
public static int multiuserMode;
public static int multiuserState = -1;
public static int suResponseType;
public static int suNotificationType;
public static int suNamespaceMode;
public static int updateChannel;
public static int repoOrder;
static {
classMap.put(MagiskManager.class, a.q.class);
classMap.put(MainActivity.class, a.b.class);
classMap.put(SplashActivity.class, a.c.class);
classMap.put(AboutActivity.class, a.d.class);
classMap.put(DonationActivity.class, a.e.class);
classMap.put(FlashActivity.class, a.f.class);
classMap.put(NoUIActivity.class, a.g.class);
classMap.put(GeneralReceiver.class, a.h.class);
classMap.put(ShortcutReceiver.class, a.i.class);
classMap.put(OnBootService.class, a.j.class);
classMap.put(UpdateCheckService.class, a.k.class);
classMap.put(AboutCardRow.class, a.l.class);
classMap.put(SuRequestActivity.class, a.m.class);
}
public static void loadMagiskInfo() {
try {
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
if (magiskVersionCode >= Const.MAGISK_VER.HIDE_STATUS) {
magiskHide = Shell.su("magiskhide --status").exec().isSuccess();
} else {
String s = ShellUtils.fastCmd(("resetprop -p ") + Const.MAGISKHIDE_PROP);
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
}
} catch (NumberFormatException ignored) {}
}
@@ -80,12 +102,12 @@ public class Data {
mm.prefs.edit().commit();
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
mm.getPackageName() + "_preferences.xml");
Shell.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS)).exec();
Shell.su(Utils.fmt("cat %s > /data/user/0/%s", xml, Const.MANAGER_CONFIGS)).exec();
}
public static void importPrefs() {
MagiskManager mm = MM();
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
SuFile config = new SuFile("/data/user/0/" + Const.MANAGER_CONFIGS);
if (config.exists()) {
SharedPreferences.Editor editor = mm.prefs.edit();
try {
@@ -150,15 +172,6 @@ public class Data {
suRequestTimeout = Utils.getPrefsInt(mm.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
suResponseType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
suNotificationType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
suAccessState = mm.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
multiuserMode = mm.mDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
suNamespaceMode = mm.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
suFingerprint = mm.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
if (suFingerprint && !FingerprintHelper.canUseFingerprint()) {
// User revoked the fingerprint
mm.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
suFingerprint = false;
}
// config
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
@@ -170,15 +183,10 @@ public class Data {
MM().prefs.edit()
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
.putBoolean(Const.Key.SU_FINGERPRINT, suFingerprint)
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
.putInt(Const.Key.REPO_ORDER, repoOrder)

View File

@@ -2,16 +2,15 @@ package com.topjohnwu.magisk;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
public class DonationActivity extends BaseActivity {
@@ -28,7 +27,7 @@ public class DonationActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donation);
ButterKnife.bind(this);
new DonationActivity_ViewBinding(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -4,8 +4,6 @@ import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
@@ -17,7 +15,6 @@ import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.CallbackList;
@@ -31,8 +28,9 @@ import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class FlashActivity extends BaseActivity {
@@ -45,11 +43,6 @@ public class FlashActivity extends BaseActivity {
private List<String> logs;
@OnClick(R.id.no_thanks)
void dismiss() {
finish();
}
@OnClick(R.id.reboot)
void reboot() {
Shell.su("/system/bin/reboot").submit();
@@ -65,7 +58,7 @@ public class FlashActivity extends BaseActivity {
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
File logFile = new File(Download.EXTERNAL_PATH, filename);
File logFile = new File(Const.EXTERNAL_PATH, filename);
try (FileWriter writer = new FileWriter(logFile)) {
for (String s : logs) {
writer.write(s);
@@ -88,7 +81,8 @@ public class FlashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash);
ButterKnife.bind(this);
new FlashActivity_ViewBinding(this);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
@@ -145,6 +139,12 @@ public class FlashActivity extends BaseActivity {
}
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button

View File

@@ -1,12 +1,10 @@
package com.topjohnwu.magisk;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.database.MagiskDB;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
@@ -22,7 +20,7 @@ public class MagiskManager extends ContainerApp {
// Global resources
public SharedPreferences prefs;
public MagiskDatabaseHelper mDB;
public MagiskDB mDB;
public RepoDatabaseHelper repoDB;
public MagiskManager() {
@@ -36,22 +34,11 @@ public class MagiskManager extends ContainerApp {
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER);
Shell.Config.verboseLogging(BuildConfig.DEBUG);
Shell.Config.setInitializer(RootUtils.class);
Shell.Config.setTimeout(2);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
mDB = MagiskDatabaseHelper.getInstance(this);
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
mDB.setStrings(Const.Key.SU_MANAGER, null);
Shell.su("pm uninstall " + pkg).exec();
}
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) {}
}
mDB = MagiskDB.getInstance();
repoDB = new RepoDatabaseHelper(this);
LocaleManager.setLocale(this);
Data.loadConfig();

View File

@@ -3,17 +3,11 @@ package com.topjohnwu.magisk;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.google.android.material.navigation.NavigationView;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.fragments.LogFragment;
import com.topjohnwu.magisk.fragments.MagiskFragment;
@@ -24,10 +18,16 @@ import com.topjohnwu.magisk.fragments.SettingsFragment;
import com.topjohnwu.magisk.fragments.SuperuserFragment;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.widget.Toolbar;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends BaseActivity
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
@@ -36,9 +36,9 @@ public class MainActivity extends BaseActivity
private int mDrawerItem;
private static boolean fromShortcut = false;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.toolbar) public Toolbar toolbar;
@BindView(R.id.nav_view) public NavigationView navigationView;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.nav_view) NavigationView navigationView;
private float toolbarElevation;
@@ -50,13 +50,13 @@ public class MainActivity extends BaseActivity
@Override
protected void onCreate(final Bundle savedInstanceState) {
if (!mm.hasInit) {
startActivity(new Intent(this, SplashActivity.class));
startActivity(new Intent(this, Data.classMap.get(SplashActivity.class)));
finish();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
new MainActivity_ViewBinding(this);
setSupportActionBar(toolbar);
@@ -112,11 +112,6 @@ public class MainActivity extends BaseActivity
return true;
}
@Override
public int[] getSubscribedTopics() {
return new int[] {Topic.RELOAD_ACTIVITY};
}
@Override
public void onPublish(int topic, Object[] result) {
recreate();
@@ -125,14 +120,12 @@ public class MainActivity extends BaseActivity
public void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED &&
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
&& Shell.rootAccess() && Data.magiskVersionCode >= 0);
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.superuser).setVisible(Shell.rootAccess() &&
!(Const.USER_ID > 0 && Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED));
menu.findItem(R.id.superuser).setVisible(Utils.showSuperUser());
}
public void navigate(String item) {
@@ -196,11 +189,11 @@ public class MainActivity extends BaseActivity
displayFragment(new SettingsFragment(), true);
break;
case R.id.app_about:
startActivity(new Intent(this, AboutActivity.class));
startActivity(new Intent(this, Data.classMap.get(AboutActivity.class)));
mDrawerItem = bak;
break;
case R.id.donation:
startActivity(new Intent(this, DonationActivity.class));
startActivity(new Intent(this, Data.classMap.get(DonationActivity.class)));
mDrawerItem = bak;
break;
}

View File

@@ -1,9 +1,9 @@
package com.topjohnwu.magisk;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.components.BaseActivity;
import androidx.annotation.NonNull;
public class NoUIActivity extends BaseActivity {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

View File

@@ -1,18 +1,18 @@
package com.topjohnwu.magisk;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.os.Build;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
@@ -22,6 +22,19 @@ public class SplashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String pkg = mm.mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(BuildConfig.APPLICATION_ID)) {
mm.mDB.setStrings(Const.Key.SU_MANAGER, null);
Shell.su("pm uninstall " + pkg).exec();
}
if (TextUtils.equals(pkg, getPackageName())) {
try {
// We are the manager, remove com.topjohnwu.magisk as it could be malware
getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
RootUtils.uninstallPkg(BuildConfig.APPLICATION_ID);
} catch (PackageManager.NameNotFoundException ignored) {}
}
// Magisk working as expected
if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
// Update check service
@@ -30,21 +43,16 @@ public class SplashActivity extends BaseActivity {
Utils.loadModules();
}
mm.repoDB = new RepoDatabaseHelper(this);
Data.importPrefs();
// Dynamic detect all locales
LocaleManager.loadAvailableLocales();
// Create notification channel on Android O
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(Const.ID.NOTIFICATION_CHANNEL,
getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT);
getSystemService(NotificationManager.class).createNotificationChannel(channel);
}
Notifications.setup(this);
// Setup shortcuts
sendBroadcast(new Intent(this, ShortcutReceiver.class));
sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
if (Download.checkNetworkStatus(this)) {
// Fire update check
@@ -58,7 +66,7 @@ public class SplashActivity extends BaseActivity {
mm.hasInit = true;
Intent intent = new Intent(this, MainActivity.class);
Intent intent = new Intent(this, Data.classMap.get(MainActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
startActivity(intent);

View File

@@ -0,0 +1,259 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.FileObserver;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.SuConnector;
import java.io.IOException;
import androidx.annotation.Nullable;
import butterknife.BindView;
public class SuRequestActivity extends BaseActivity {
@BindView(R.id.su_popup) LinearLayout suPopup;
@BindView(R.id.timeout) Spinner timeout;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appNameView;
@BindView(R.id.package_name) TextView packageNameView;
@BindView(R.id.grant_btn) Button grant_btn;
@BindView(R.id.deny_btn) Button deny_btn;
@BindView(R.id.fingerprint) ImageView fingerprintImg;
@BindView(R.id.warning) TextView warning;
private SuConnector connector;
private Policy policy;
private CountDownTimer timer;
private FingerprintHelper fingerprintHelper;
class SuConnectorV1 extends SuConnector {
SuConnectorV1(String name) throws IOException {
super(name);
}
@Override
public void connect(String name) throws IOException {
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.FILESYSTEM));
new FileObserver(name) {
@Override
public void onEvent(int fileEvent, String path) {
if (fileEvent == FileObserver.DELETE_SELF) {
finish();
}
}
}.startWatching();
}
@Override
public void onResponse() throws IOException {
out.write((policy.policy == Policy.ALLOW ? "socket:ALLOW" : "socket:DENY").getBytes());
}
}
class SuConnectorV2 extends SuConnector {
SuConnectorV2(String name) throws IOException {
super(name);
}
@Override
public void connect(String name) throws IOException {
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT));
}
@Override
public void onResponse() throws IOException {
out.writeInt(policy.policy);
}
}
@Override
public int getDarkTheme() {
return R.style.SuRequest_Dark;
}
@Override
public void finish() {
if (timer != null)
timer.cancel();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
super.finish();
}
@Override
public void onBackPressed() {
if (policy != null) {
handleAction(Policy.DENY);
} else {
finish();
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
PackageManager pm = getPackageManager();
mm.mDB.clearOutdated();
// Get policy
Intent intent = getIntent();
try {
String socketName = intent.getStringExtra("socket");
connector = intent.getIntExtra("version", 1) == 1 ?
new SuConnectorV1(socketName) : new SuConnectorV2(socketName);
Bundle bundle = connector.readSocketInput();
int uid = Integer.parseInt(bundle.getString("uid"));
policy = mm.mDB.getPolicy(uid);
if (policy == null) {
policy = new Policy(uid, pm);
}
} catch (IOException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
finish();
return;
}
// Never allow com.topjohnwu.magisk (could be malware)
if (TextUtils.equals(policy.packageName, BuildConfig.APPLICATION_ID)) {
finish();
return;
}
switch (Data.suResponseType) {
case Const.Value.SU_AUTO_DENY:
handleAction(Policy.DENY, 0);
return;
case Const.Value.SU_AUTO_ALLOW:
handleAction(Policy.ALLOW, 0);
return;
case Const.Value.SU_PROMPT:
default:
}
// If not interactive, response directly
if (policy.policy != Policy.INTERACTIVE) {
handleAction();
return;
}
setContentView(R.layout.activity_request);
new SuRequestActivity_ViewBinding(this);
appIcon.setImageDrawable(policy.info.loadIcon(pm));
appNameView.setText(policy.appName);
packageNameView.setText(policy.packageName);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.allow_timeout, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
timeout.setAdapter(adapter);
timer = new CountDownTimer(Data.suRequestTimeout * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")"));
}
@Override
public void onFinish() {
deny_btn.setText(getString(R.string.deny_with_str, "(0)"));
handleAction(Policy.DENY);
}
};
boolean useFP = FingerprintHelper.useFingerPrint();
if (useFP) {
try {
fingerprintHelper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
warning.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
warning.setText(helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
handleAction(Policy.ALLOW);
}
@Override
public void onAuthenticationFailed() {
warning.setText(R.string.auth_fail);
}
};
fingerprintHelper.authenticate();
} catch (Exception e) {
e.printStackTrace();
useFP = false;
}
}
if (!useFP) {
grant_btn.setOnClickListener(v -> {
handleAction(Policy.ALLOW);
timer.cancel();
});
grant_btn.requestFocus();
}
grant_btn.setVisibility(useFP ? View.GONE : View.VISIBLE);
fingerprintImg.setVisibility(useFP ? View.VISIBLE : View.GONE);
deny_btn.setOnClickListener(v -> {
handleAction(Policy.DENY);
timer.cancel();
});
suPopup.setOnClickListener(v -> cancelTimeout());
timeout.setOnTouchListener((v, event) -> cancelTimeout());
timer.start();
}
private boolean cancelTimeout() {
timer.cancel();
deny_btn.setText(getString(R.string.deny));
return false;
}
private void handleAction() {
connector.response();
finish();
}
private void handleAction(int action) {
handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]);
}
private void handleAction(int action, int time) {
policy.policy = action;
if (time >= 0) {
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
mm.mDB.updatePolicy(policy);
}
handleAction();
}
}

View File

@@ -3,11 +3,7 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -19,8 +15,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@@ -28,8 +24,9 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
@@ -53,25 +50,12 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return new ViewHolder(v);
}
private String getLabel(ApplicationInfo info) {
if (info.labelRes > 0) {
try {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
}
return info.loadLabel(pm).toString();
}
private void loadApps() {
fullList = pm.getInstalledApplications(0);
hideList = Shell.su("magiskhide --ls").exec().getOut();
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) {
i.remove();
}
}
@@ -79,7 +63,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
boolean ah = hideList.contains(a.packageName);
boolean bh = hideList.contains(b.packageName);
if (ah == bh) {
return getLabel(a).toLowerCase().compareTo(getLabel(b).toLowerCase());
return Utils.getAppLabel(a, pm).toLowerCase()
.compareTo(Utils.getAppLabel(b, pm).toLowerCase());
} else if (ah) {
return -1;
} else {
@@ -94,7 +79,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ApplicationInfo info = showList.get(position);
holder.appIcon.setImageDrawable(info.loadIcon(pm));
holder.appName.setText(getLabel(info));
holder.appName.setText(Utils.getAppLabel(info, pm));
holder.appPackage.setText(info.packageName);
holder.checkBox.setOnCheckedChangeListener(null);
@@ -132,11 +117,11 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ApplicationAdapter$ViewHolder_ViewBinding(this, itemView);
}
}
private class ApplicationFilter extends Filter {
class ApplicationFilter extends Filter {
private boolean lowercaseContains(String s, CharSequence filter) {
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
@@ -150,7 +135,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
showList = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : fullList) {
if (lowercaseContains(getLabel(info), filter)
if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|| lowercaseContains(info.packageName, filter)) {
showList.add(info);
}

View File

@@ -1,8 +1,6 @@
package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -11,6 +9,7 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Module;
@@ -18,8 +17,9 @@ import com.topjohnwu.superuser.Shell;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
@@ -29,6 +29,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
mList = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
@@ -46,9 +47,9 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
String noInfo = context.getString(R.string.no_info_provided);
holder.title.setText(module.getName());
holder.versionName.setText( TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText( TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText( TextUtils.isEmpty(description) ? noInfo : description);
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(module.isEnabled());
@@ -114,7 +115,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ModulesAdapter$ViewHolder_ViewBinding(this, itemView);
if (!Shell.rootAccess()) {
checkBox.setEnabled(false);

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.adapters;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,33 +9,37 @@ import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.database.MagiskDB;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
private List<Policy> policyList;
private MagiskDatabaseHelper dbHelper;
private MagiskDB dbHelper;
private PackageManager pm;
private Set<Policy> expandList = new HashSet<>();
public PolicyAdapter(List<Policy> list, MagiskDatabaseHelper db, PackageManager pm) {
public PolicyAdapter(List<Policy> list, MagiskDB db, PackageManager pm) {
policyList = list;
dbHelper = db;
this.pm = pm;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
@@ -63,14 +65,34 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
holder.appName.setText(policy.appName);
holder.packageName.setText(policy.packageName);
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && policy.policy == Policy.DENY) ||
(!isChecked && policy.policy == Policy.ALLOW)) {
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
holder.notificationSwitch.setOnCheckedChangeListener(null);
holder.loggingSwitch.setOnCheckedChangeListener(null);
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
holder.masterSwitch.setOnClickListener(v -> {
boolean isChecked = holder.masterSwitch.isChecked();
Runnable r = () -> {
if ((isChecked && policy.policy == Policy.DENY) ||
(!isChecked && policy.policy == Policy.ALLOW)) {
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
}
};
if (FingerprintHelper.useFingerPrint()) {
holder.masterSwitch.setChecked(!isChecked);
FingerprintHelper.showAuthDialog((Activity) v.getContext(), () -> {
holder.masterSwitch.setChecked(isChecked);
r.run();
});
} else {
r.run();
}
});
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
@@ -107,9 +129,6 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
.setNegativeButton(R.string.no_thanks, null)
.setCancelable(true)
.show());
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
// Hide for now
holder.moreInfo.setVisibility(View.GONE);
@@ -137,7 +156,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new PolicyAdapter$ViewHolder_ViewBinding(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}

View File

@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Pair;
import android.view.LayoutInflater;
@@ -13,8 +12,8 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.DownloadModule;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.container.Module;
@@ -25,8 +24,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> {
@@ -89,11 +88,16 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
Repo repo = repoPairs.get(section).second.get(position);
Context context = holder.itemView.getContext();
holder.title.setText(repo.getName());
holder.versionName.setText(repo.getVersion());
String name = repo.getName();
String version = repo.getVersion();
String author = repo.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(repo.getDescription());
String description = repo.getDescription();
String noInfo = context.getString(R.string.no_info_provided);
holder.title.setText(TextUtils.isEmpty(name) ? noInfo : name);
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
holder.infoLayout.setOnClickListener(v ->
@@ -101,16 +105,15 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
holder.downloadImage.setOnClickListener(v -> {
new CustomAlertDialog((BaseActivity) context)
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
.setCancelable(true)
.setPositiveButton(R.string.install, (d, i) ->
new ProcessRepoZip((BaseActivity) context, repo, true).exec()
)
.setNeutralButton(R.string.download, (d, i) ->
new ProcessRepoZip((BaseActivity) context, repo, false).exec())
.setNegativeButton(R.string.no_thanks, null)
.show();
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
.setCancelable(true)
.setPositiveButton(R.string.install, (d, i) ->
DownloadModule.exec((BaseActivity) context, repo, true))
.setNeutralButton(R.string.download, (d, i) ->
DownloadModule.exec((BaseActivity) context, repo, false))
.setNegativeButton(R.string.no_thanks, null)
.show();
});
}
@@ -165,7 +168,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
SectionHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ReposAdapter$SectionHolder_ViewBinding(this, itemView);
}
}
@@ -181,7 +184,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
RepoHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ReposAdapter$RepoHolder_ViewBinding(this, itemView);
}
}

View File

@@ -1,15 +1,18 @@
package com.topjohnwu.magisk.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int SECTION_TYPE = Integer.MIN_VALUE;
@NonNull
@Override
final public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == SECTION_TYPE)
return onCreateSectionViewHolder(parent);
return onCreateItemViewHolder(parent, viewType);
@@ -17,7 +20,7 @@ public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C exte
@Override
@SuppressWarnings("unchecked")
final public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
PositionInfo info = getPositionInfo(position);
if (info.position == -1)
onBindSectionViewHolder((S) holder, info.section);

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.adapters;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -13,38 +11,37 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.database.MagiskDB;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
private List<List<Integer>> logEntryList;
private List<List<SuLogEntry>> logEntries;
private Set<Integer> itemExpanded, sectionExpanded;
private MagiskDatabaseHelper suDB;
private Cursor suLogCursor = null;
private MagiskDB suDB;
public SuLogAdapter(MagiskDatabaseHelper db) {
public SuLogAdapter(MagiskDB db) {
suDB = db;
logEntryList = Collections.emptyList();
logEntries = Collections.emptyList();
sectionExpanded = new HashSet<>();
itemExpanded = new HashSet<>();
}
@Override
public int getSectionCount() {
return logEntryList.size();
return logEntries.size();
}
@Override
public int getItemCount(int section) {
return sectionExpanded.contains(section) ? logEntryList.get(section).size() : 0;
return sectionExpanded.contains(section) ? logEntries.get(section).size() : 0;
}
@Override
@@ -61,8 +58,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
@Override
public void onBindSectionViewHolder(SectionHolder holder, int section) {
suLogCursor.moveToPosition(logEntryList.get(section).get(0));
SuLogEntry entry = new SuLogEntry(suLogCursor);
SuLogEntry entry = logEntries.get(section).get(0);
holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0);
holder.itemView.setOnClickListener(v -> {
RotateAnimation rotate;
@@ -70,11 +66,11 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
holder.arrow.setRotation(0);
rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
sectionExpanded.remove(section);
notifyItemRangeRemoved(getItemPosition(section, 0), logEntryList.get(section).size());
notifyItemRangeRemoved(getItemPosition(section, 0), logEntries.get(section).size());
} else {
rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
sectionExpanded.add(section);
notifyItemRangeInserted(getItemPosition(section, 0), logEntryList.get(section).size());
notifyItemRangeInserted(getItemPosition(section, 0), logEntries.get(section).size());
}
rotate.setDuration(300);
rotate.setFillAfter(true);
@@ -85,17 +81,16 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
@Override
public void onBindItemViewHolder(LogViewHolder holder, int section, int position) {
int sqlPosition = logEntryList.get(section).get(position);
suLogCursor.moveToPosition(sqlPosition);
SuLogEntry entry = new SuLogEntry(suLogCursor);
holder.setExpanded(itemExpanded.contains(sqlPosition));
SuLogEntry entry = logEntries.get(section).get(position);
int realIdx = getItemPosition(section, position);
holder.setExpanded(itemExpanded.contains(realIdx));
holder.itemView.setOnClickListener(view -> {
if (holder.isExpanded()) {
holder.collapse();
itemExpanded.remove(sqlPosition);
itemExpanded.remove(realIdx);
} else {
holder.expand();
itemExpanded.add(sqlPosition);
itemExpanded.add(realIdx);
}
});
holder.appName.setText(entry.appName);
@@ -107,10 +102,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
}
public void notifyDBChanged() {
if (suLogCursor != null)
suLogCursor.close();
suLogCursor = suDB.getLogCursor();
logEntryList = suDB.getLogStructure();
logEntries = suDB.getLogs();
itemExpanded.clear();
sectionExpanded.clear();
sectionExpanded.add(0);
@@ -124,7 +116,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
SectionHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new SuLogAdapter$SectionHolder_ViewBinding(this, itemView);
}
}
@@ -142,7 +134,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
LogViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}

View File

@@ -1,13 +1,13 @@
package com.topjohnwu.magisk.adapters;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class TabFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;

View File

@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
@@ -32,7 +33,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Void> {
private void dlSnet() throws Exception {
Shell.sh("rm -rf " + dexPath.getParent()).exec();
dexPath.getParentFile().mkdir();
HttpURLConnection conn = WebService.mustRequest(Data.snetLink, null);
HttpURLConnection conn = WebService.mustRequest(Const.Url.SNET_URL);
try (
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
InputStream in = new BufferedInputStream(conn.getInputStream())) {
@@ -51,7 +52,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Void> {
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
(ISafetyNetHelper.Callback) code ->
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() < Data.snetVersionCode) {
if (helper.getVersion() < Const.SNET_EXT_VER) {
throw new Exception();
}
}

View File

@@ -1,13 +1,13 @@
package com.topjohnwu.magisk.asyncs;
import android.os.AsyncTask;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.JSONObjectRequestListener;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.NotificationMgr;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONException;
import org.json.JSONObject;
@@ -42,64 +42,66 @@ public class CheckUpdates {
}
}
public static void fetchUpdates() {
String jsonStr = "";
public static void check(Runnable cb) {
String url;
switch (Data.updateChannel) {
case Const.Value.STABLE_CHANNEL:
jsonStr = WebService.getString(Const.Url.STABLE_URL);
url = Const.Url.STABLE_URL;
break;
case Const.Value.BETA_CHANNEL:
jsonStr = WebService.getString(Const.Url.BETA_URL);
url = Const.Url.BETA_URL;
break;
case Const.Value.CUSTOM_CHANNEL:
jsonStr = WebService.getString(Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
url = Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
break;
default:
return;
}
JSONObject json;
try {
json = new JSONObject(jsonStr);
} catch (JSONException e) {
return;
}
JSONObject magisk = getJson(json, "magisk");
Data.remoteMagiskVersionString = getString(magisk, "version", null);
Data.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
Data.magiskLink = getString(magisk, "link", null);
Data.magiskNoteLink = getString(magisk, "note", null);
Data.magiskMD5 = getString(magisk, "md5", null);
JSONObject manager = getJson(json, "app");
Data.remoteManagerVersionString = getString(manager, "version", null);
Data.remoteManagerVersionCode = getInt(manager, "versionCode", -1);
Data.managerLink = getString(manager, "link", null);
Data.managerNoteLink = getString(manager, "note", null);
JSONObject uninstaller = getJson(json, "uninstaller");
Data.uninstallerLink = getString(uninstaller, "link", null);
JSONObject snet = getJson(json, "snet");
Data.snetVersionCode = getInt(snet, "versionCode", -1);
Data.snetLink = getString(snet, "link", null);
}
public static void check(Runnable cb) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
fetchUpdates();
if (cb != null) {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
NotificationMgr.managerUpdate();
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
NotificationMgr.magiskUpdate();
}
cb.run();
}
Topic.publish(Topic.UPDATE_CHECK_DONE);
});
AndroidNetworking.get(url).build().getAsJSONObject(new UpdateListener(cb));
}
public static void check() {
check(null);
}
private static class UpdateListener implements JSONObjectRequestListener {
private Runnable cb;
UpdateListener(Runnable callback) {
cb = callback;
}
@Override
public void onResponse(JSONObject json) {
JSONObject magisk = getJson(json, "magisk");
Data.remoteMagiskVersionString = getString(magisk, "version", null);
Data.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
Data.magiskLink = getString(magisk, "link", null);
Data.magiskNoteLink = getString(magisk, "note", null);
Data.magiskMD5 = getString(magisk, "md5", null);
JSONObject manager = getJson(json, "app");
Data.remoteManagerVersionString = getString(manager, "version", null);
Data.remoteManagerVersionCode = getInt(manager, "versionCode", -1);
Data.managerLink = getString(manager, "link", null);
Data.managerNoteLink = getString(manager, "note", null);
JSONObject uninstaller = getJson(json, "uninstaller");
Data.uninstallerLink = getString(uninstaller, "link", null);
if (cb != null) {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
Notifications.managerUpdate();
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
Notifications.magiskUpdate();
}
cb.run();
}
Topic.publish(Topic.UPDATE_CHECK_DONE);
}
@Override
public void onError(ANError anError) {}
}
}

View File

@@ -0,0 +1,125 @@
package com.topjohnwu.magisk.asyncs;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class DownloadModule {
public static void exec(BaseActivity activity, Repo repo, boolean install) {
activity.runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> dlProcessInstall(repo, install)));
}
private static void dlProcessInstall(Repo repo, boolean install) {
File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
ProgressNotification progress = new ProgressNotification(output.getName());
try {
MagiskManager mm = Data.MM();
HttpURLConnection conn = WebService.mustRequest(repo.getZipUrl());
ProgressInputStream pis = new ProgressInputStream(conn.getInputStream(),
conn.getContentLength(), progress);
removeTopFolder(new BufferedInputStream(pis),
new BufferedOutputStream(new FileOutputStream(output)));
conn.disconnect();
if (install) {
progress.dismiss();
Intent intent = new Intent(mm, Data.classMap.get(FlashActivity.class));
intent.setData(Uri.fromFile(output))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
mm.startActivity(intent);
} else {
progress.getNotification().setContentTitle(output.getName());
progress.dlDone();
}
} catch (Exception e) {
e.printStackTrace();
progress.dlFail();
}
}
private static void removeTopFolder(InputStream in, OutputStream out) throws IOException {
try (ZipInputStream zin = new ZipInputStream(in);
ZipOutputStream zout = new ZipOutputStream(out)) {
ZipEntry entry;
int off = -1;
while ((entry = zin.getNextEntry()) != null) {
if (off < 0)
off = entry.getName().indexOf('/') + 1;
String path = entry.getName().substring(off);
if (path.isEmpty())
continue;
zout.putNextEntry(new ZipEntry(path));
if (!entry.isDirectory())
ShellUtils.pump(zin, zout);
}
}
}
private static class ProgressInputStream extends FilterInputStream {
private long totalBytes;
private long bytesDownloaded;
private ProgressNotification progress;
protected ProgressInputStream(InputStream in, long size, ProgressNotification p) {
super(in);
totalBytes = size;
progress = p;
}
private void updateProgress() {
progress.onProgress(bytesDownloaded, totalBytes);
}
@Override
public int read() throws IOException {
int b = super.read();
if (b >= 0) {
bytesDownloaded++;
updateProgress();
}
return b;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int sz = super.read(b, off, len);
if (sz > 0) {
bytesDownloaded += sz;
updateProgress();
}
return sz;
}
}
}

View File

@@ -4,7 +4,6 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
@@ -15,7 +14,6 @@ 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.Download;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.magisk.utils.ZipUtils;
@@ -44,6 +42,8 @@ import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.List;
import androidx.annotation.NonNull;
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private static final int PATCH_MODE = 0;
@@ -135,7 +135,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
console.add("- Downloading zip");
HttpURLConnection conn = WebService.mustRequest(Data.magiskLink, null);
HttpURLConnection conn = WebService.mustRequest(Data.magiskLink);
buf = new BufferedInputStream(new ProgressStream(conn), conn.getContentLength());
buf.mark(conn.getContentLength() + 1);
try (OutputStream out = new FileOutputStream(zip)) {
@@ -242,7 +242,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
switch (mode) {
case PATCH_MODE:
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
File dest = new File(Download.EXTERNAL_PATH, "patched_boot" + fmt);
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + fmt);
dest.getParentFile().mkdirs();
OutputStream out;
switch (fmt) {

View File

@@ -1,13 +1,12 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.support.v7.app.AlertDialog;
import android.webkit.WebView;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.ShellUtils;
import org.commonmark.node.Node;
@@ -18,6 +17,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import androidx.appcompat.app.AlertDialog;
public class MarkDownWindow extends ParallelTask<Void, Void, String> {
private String mTitle;
@@ -42,7 +43,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
MagiskManager mm = Data.MM();
String md;
if (mUrl != null) {
md = WebService.getString(mUrl);
md = Utils.dlString(mUrl);
} else {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
ShellUtils.pump(is, out);

View File

@@ -1,41 +1,53 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.widget.Toast;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils;
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.SignAPK;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.security.SecureRandom;
import java.util.jar.JarEntry;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
public class PatchAPK {
public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
public static final String UPPERALPHA = LOWERALPHA.toUpperCase();
public static final String ALPHA = LOWERALPHA + UPPERALPHA;
public static final String DIGITS = "0123456789";
public static final String ALPHANUM = ALPHA + DIGITS;
public static final String ALPHANUMDOTS = ALPHANUM + "............";
private static 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';
char next, prev = '.';
for (int i = 0; i < length; ++i) {
if (prev == '.' || i == length - 1 || i == 0) {
next = alpha.charAt(random.nextInt(alpha.length()));
if (prev == '.' || i == length - 1) {
next = ALPHA.charAt(random.nextInt(ALPHA.length()));
} else {
next = full.charAt(random.nextInt(full.length()));
next = ALPHANUMDOTS.charAt(random.nextInt(ALPHANUMDOTS.length()));
}
builder.append(next);
prev = next;
@@ -43,88 +55,81 @@ public class PatchAPK {
return builder.toString();
}
private static int findOffset(byte buf[], byte pattern[]) {
private static boolean findAndPatch(byte xml[], String a, String b) {
if (a.length() != b.length())
return false;
char[] from = a.toCharArray(), to = b.toCharArray();
CharBuffer buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer();
int offset = -1;
for (int i = 0; i < buf.length - pattern.length; ++i) {
for (int i = 0; i < buf.length() - from.length; ++i) {
boolean match = true;
for (int j = 0; j < pattern.length; ++j) {
if (buf[i + j] != pattern[j]) {
for (int j = 0; j < from.length; ++j) {
if (buf.get(i + j) != from[j]) {
match = false;
break;
}
}
if (match) {
// Make sure it is null terminated
if (match && buf.get(i + from.length) == '\0') {
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);
buf.position(offset);
buf.put(to);
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;
private static boolean findAndPatch(byte xml[], int a, int b) {
IntBuffer buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
int len = xml.length / 4;
for (int i = 0; i < len; ++i) {
if (buf.get(i) == a) {
buf.put(i, b);
return true;
}
}
return false;
}
private static boolean patchAndHide() {
MagiskManager mm = Data.MM();
// 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());
File repack = new File(mm.getFilesDir(), "patched.apk");
String pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length());
try {
JarMap apk = new JarMap(mm.getPackageCodePath());
if (!patchPackageID(apk, Const.ORIG_PKG_NAME, pkg))
if (!patch(apk, pkg))
return false;
SignAPK.sign(apk, new SuFileOutputStream(repack));
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(repack)));
} catch (Exception e) {
return false;
}
// Install the application
repack.setReadable(true, false);
if (!ShellUtils.fastCmdResult("pm install " + repack))
return false;
repack.delete();
return false;;
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
Data.exportPrefs();
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, pkg);
return true;
}
public static boolean patchPackageID(JarMap apk, String from, String to) {
public static boolean patch(JarMap apk, String pkg) {
try {
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"))
if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) ||
!findAndPatch(xml, BuildConfig.APPLICATION_ID + ".provider", pkg + ".provider") ||
!findAndPatch(xml, R.string.app_name, R.string.re_app_name))
return false;
// Write in changes
@@ -136,18 +141,16 @@ public class PatchAPK {
return true;
}
public static void hideManager(Activity activity) {
ProgressDialog dialog = ProgressDialog.show(activity,
activity.getString(R.string.hide_manager_toast),
activity.getString(R.string.hide_manager_toast2));
public static void hideManager() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
MagiskManager mm = Data.MM();
NotificationCompat.Builder progress =
Notifications.progress(mm.getString(R.string.hide_manager_title));
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build());
boolean b = patchAndHide();
Data.mainHandler.post(() -> {
dialog.cancel();
if (!b) {
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
}
});
mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);
if (!b) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
});
}
}

View File

@@ -1,196 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.magisk.utils.ZipUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
private ProgressDialog progressDialog;
private boolean mInstall;
private File mFile;
private Repo mRepo;
private int progress = 0, total = -1;
public ProcessRepoZip(BaseActivity context, Repo repo, boolean install) {
super(context);
mRepo = repo;
mInstall = install && Shell.rootAccess();
mFile = new File(Download.EXTERNAL_PATH, repo.getDownloadFilename());
}
private void removeTopFolder(File input, File output) throws IOException {
JarEntry entry;
try (
JarInputStream in = new JarInputStream(new BufferedInputStream(new FileInputStream(input)));
JarOutputStream out = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(output)))
) {
String path;
while ((entry = in.getNextJarEntry()) != null) {
// Remove the top directory from the path
path = entry.getName().substring(entry.getName().indexOf("/") + 1);
// If it's the top folder, ignore it
if (path.isEmpty()) {
continue;
}
// Don't include placeholder
if (path.equals("system/placeholder")) {
continue;
}
out.putNextEntry(new JarEntry(path));
ShellUtils.pump(in, out);
}
}
}
@Override
protected BaseActivity getActivity() {
return (BaseActivity) super.getActivity();
}
@Override
protected void onPreExecute() {
BaseActivity activity = getActivity();
mFile.getParentFile().mkdirs();
progressDialog = ProgressDialog.show(activity, activity.getString(R.string.zip_download_title), activity.getString(R.string.zip_download_msg, 0));
}
@Override
protected Boolean doInBackground(Void... params) {
BaseActivity activity = getActivity();
if (activity == null) return null;
try {
// Request zip from Internet
HttpURLConnection conn = WebService.mustRequest(mRepo.getZipUrl(), null);
total = conn.getContentLength();
// Temp files
File temp1 = new File(activity.getCacheDir(), "1.zip");
File temp2 = new File(temp1.getParentFile(), "2.zip");
temp1.getParentFile().mkdir();
// First download the zip, Web -> temp1
try (
InputStream in = new BufferedInputStream(new ProgressInputStream(conn.getInputStream()));
OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))
) {
ShellUtils.pump(in, out);
in.close();
}
conn.disconnect();
Data.mainHandler.post(() -> {
progressDialog.setTitle(R.string.zip_process_title);
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
});
// First remove top folder in Github source zip, temp1 -> temp2
removeTopFolder(temp1, temp2);
// Then sign the zip
ZipUtils.signZip(temp2, mFile);
// Delete temp files
temp1.delete();
temp2.delete();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
BaseActivity activity = getActivity();
if (activity == null) return;
progressDialog.dismiss();
if (result) {
Uri uri = Uri.fromFile(mFile);
if (mInstall) {
Intent intent = new Intent(activity, FlashActivity.class);
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
activity.startActivity(intent);
} else {
SnackbarMaker.showUri(activity, uri);
}
} else {
Utils.toast(R.string.process_error, Toast.LENGTH_LONG);
}
super.onPostExecute(result);
}
@Override
public void exec(Void... voids) {
getActivity().runWithPermission(
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, super::exec);
}
private class ProgressInputStream extends FilterInputStream {
ProgressInputStream(InputStream in) {
super(in);
}
private void updateDlProgress(int step) {
progress += step;
progressDialog.setMessage(getActivity().getString(R.string.zip_download_msg,
(int) (100 * (double) progress / total + 0.5)));
}
@Override
public synchronized int read() throws IOException {
int b = super.read();
if (b > 0) {
Data.mainHandler.post(() -> updateDlProgress(1));
}
return b;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
int read = super.read(b, off, len);
if (read > 0) {
Data.mainHandler.post(() -> updateDlProgress(read));
}
return read;
}
}
}

View File

@@ -3,14 +3,15 @@ package com.topjohnwu.magisk.asyncs;
import android.database.Cursor;
import android.os.AsyncTask;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.common.ANRequest;
import com.androidnetworking.common.ANResponse;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONArray;
import org.json.JSONException;
@@ -22,9 +23,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
@@ -32,51 +31,45 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class UpdateRepos {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
private static final DateFormat dateFormat;
private MagiskManager mm;
private Set<String> cached;
private ExecutorService threadPool;
static {
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
private MagiskManager mm;
private Set<String> cached;
private ExecutorService threadPool;
public UpdateRepos() {
mm = Data.MM();
}
private void waitTasks() {
threadPool.shutdown();
try {
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {}
while (true) {
try {
if (threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS))
break;
} catch (InterruptedException ignored) {}
}
}
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
JSONArray jsonArray = new JSONArray(jsonString);
// Empty page, halt
if (jsonArray.length() == 0)
return false;
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject rawRepo = jsonArray.getJSONObject(i);
String id = rawRepo.getString("description");
String name = rawRepo.getString("name");
private void loadJSON(JSONArray array) throws JSONException, ParseException {
for (int i = 0; i < array.length(); i++) {
JSONObject rawRepo = array.getJSONObject(i);
String id = rawRepo.getString("name");
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
Set<String> set = Collections.synchronizedSet(cached);
threadPool.execute(() -> {
Repo repo = mm.repoDB.getRepo(id);
try {
if (repo == null)
repo = new Repo(name);
repo = new Repo(id);
else
set.remove(id);
cached.remove(id);
repo.update(date);
mm.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
@@ -85,42 +78,50 @@ public class UpdateRepos {
}
});
}
return true;
}
/* We sort repos by last push, which means that we only need to check whether the
* first page is updated to determine whether the online repo database is changed
*/
private boolean loadPage(int page) {
Map<String, String> header = new HashMap<>();
if (page == 0)
header.put(Const.Key.IF_NONE_MATCH, mm.prefs.getString(Const.Key.ETAG_KEY, ""));
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
ANRequest.GetRequestBuilder req = AndroidNetworking.get(Const.Url.REPO_URL)
.addQueryParameter("page", String.valueOf(page + 1));
if (page == 0) {
String etag = mm.prefs.getString(Const.Key.ETAG_KEY, null);
if (etag != null)
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
}
ANResponse<JSONArray> res = req.build().executeForJSONArray();
if (res.getOkHttpResponse().code() == HttpURLConnection.HTTP_NOT_MODIFIED)
return false;
// Current page is the last page
if (res.getResult() == null || res.getResult().length() == 0)
return true;
try {
HttpURLConnection conn = WebService.request(url, header);
// No updates
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED)
return false;
// Current page is the last page
if (!loadJSON(WebService.getString(conn)))
return true;
} catch (Exception e) {
loadJSON(res.getResult());
} catch (JSONException | ParseException e) {
// Should not happen, but if exception occurs, page load fails
return false;
}
// Update ETAG
if (page == 0) {
String etag = header.get(Const.Key.ETAG_KEY);
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
String etag = res.getOkHttpResponse().header(Const.Key.ETAG_KEY);
if (etag != null) {
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
}
}
String links = header.get(Const.Key.LINK_KEY);
String links = res.getOkHttpResponse().header(Const.Key.LINK_KEY);
return links == null || !links.contains("next") || loadPage(page + 1);
}
private boolean loadPages() {
return loadPage(0);
}
private void fullReload() {
Cursor c = mm.repoDB.getRawCursor();
while (c.moveToNext()) {
@@ -141,10 +142,10 @@ public class UpdateRepos {
public void exec(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
cached = mm.repoDB.getRepoIDSet();
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
if (loadPage(0)) {
if (loadPages()) {
waitTasks();
// The leftover cached means they are removed from online repo
mm.repoDB.removeRepo(cached);

View File

@@ -29,7 +29,6 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* @author dvdandroid
@@ -52,7 +51,7 @@ public class AboutCardRow extends LinearLayout {
public AboutCardRow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.info_item_row, this);
ButterKnife.bind(this, this);
new AboutCardRow_ViewBinding(this, this);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
String title;
@@ -69,8 +68,6 @@ public class AboutCardRow extends LinearLayout {
@Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
mView.setOnClickListener(l);
}

View File

@@ -2,34 +2,37 @@ package com.topjohnwu.magisk.components;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.NoUIActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
public abstract class FlavorActivity extends AppCompatActivity implements Topic.AutoSubscriber {
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public abstract class BaseActivity extends AppCompatActivity implements Topic.AutoSubscriber {
public static final String INTENT_PERM = "perm_dialog";
protected static Runnable permissionGrantCallback;
static int[] EMPTY_INT_ARRAY = new int[0];
private ActivityResultListener activityResultListener;
static int[] EMPTY_INT_ARRAY = new int[0];
public MagiskManager mm;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Configuration config = base.getResources().getConfiguration();
config.setLocale(LocaleManager.locale);
applyOverrideConfiguration(config);
mm = Data.MM();
}
@Override
public int[] getSubscribedTopics() {
return EMPTY_INT_ARRAY;
@@ -40,13 +43,25 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
return -1;
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Configuration config = base.getResources().getConfiguration();
config.setLocale(LocaleManager.locale);
applyOverrideConfiguration(config);
mm = Data.MM();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Topic.subscribe(this);
if (Data.isDarkTheme && getDarkTheme() != -1) {
setTheme(getDarkTheme());
}
super.onCreate(savedInstanceState);
String[] perms = getIntent().getStringArrayExtra(INTENT_PERM);
if (perms != null)
ActivityCompat.requestPermissions(this, perms, 0);
}
@Override
@@ -69,6 +84,34 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
}
}
public static void runWithPermission(Context context, String[] permissions, Runnable callback) {
boolean granted = true;
for (String perm : permissions) {
if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED)
granted = false;
}
if (granted) {
Const.EXTERNAL_PATH.mkdirs();
callback.run();
} else {
// Passed in context should be an activity if not granted, need to show dialog!
permissionGrantCallback = callback;
if (!(context instanceof BaseActivity)) {
// Start NoUIActivity to show dialog
Intent intent = new Intent(context, Data.classMap.get(NoUIActivity.class));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(INTENT_PERM, permissions);
context.startActivity(intent);
} else {
ActivityCompat.requestPermissions((BaseActivity) context, permissions, 0);
}
}
}
public void runWithPermission(String[] permissions, Runnable callback) {
runWithPermission(this, permissions, callback);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (activityResultListener != null)
@@ -81,6 +124,23 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
super.startActivityForResult(intent, requestCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean grant = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED)
grant = false;
}
if (grant) {
if (permissionGrantCallback != null) {
permissionGrantCallback.run();
}
} else {
Toast.makeText(this, R.string.no_rw_storage, Toast.LENGTH_LONG).show();
}
permissionGrantCallback = null;
}
public interface ActivityResultListener {
void onActivityResult(int requestCode, int resultCode, Intent data);
}

View File

@@ -1,15 +1,18 @@
package com.topjohnwu.magisk.components;
import android.content.Intent;
import android.support.v4.app.Fragment;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic;
import androidx.fragment.app.Fragment;
import butterknife.Unbinder;
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
public MagiskManager mm;
protected Unbinder unbinder = null;
public BaseFragment() {
mm = Data.MM();
@@ -27,6 +30,13 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (unbinder != null)
unbinder.unbind();
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, this::onActivityResult);
@@ -42,6 +52,6 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
@Override
public int[] getSubscribedTopics() {
return FlavorActivity.EMPTY_INT_ARRAY;
return BaseActivity.EMPTY_INT_ARRAY;
}
}

View File

@@ -2,11 +2,6 @@ package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.annotation.StyleRes;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@@ -15,8 +10,12 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AlertDialog;
import butterknife.BindView;
import butterknife.ButterKnife;
public class CustomAlertDialog extends AlertDialog.Builder {
@@ -37,7 +36,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@BindView(R.id.neutral) public Button neutral;
ViewHolder(View v) {
ButterKnife.bind(this, v);
new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
messageView.setVisibility(View.GONE);
negative.setVisibility(View.GONE);
positive.setVisibility(View.GONE);

View File

@@ -1,11 +1,12 @@
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import androidx.annotation.NonNull;
public class EnvFixDialog extends CustomAlertDialog {
public EnvFixDialog(@NonNull Activity activity) {

View File

@@ -1,22 +1,24 @@
package com.topjohnwu.magisk.components;
import android.content.Context;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.DownloadListener;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Utils;
import java.util.List;
import androidx.appcompat.app.AlertDialog;
class InstallMethodDialog extends AlertDialog.Builder {
InstallMethodDialog(BaseActivity activity, List<String> options) {
@@ -26,54 +28,78 @@ class InstallMethodDialog extends AlertDialog.Builder {
Intent intent;
switch (idx) {
case 1:
if (Data.remoteMagiskVersionCode < 1400) {
SnackbarMaker.make(activity, R.string.no_boot_file_patch_support,
Snackbar.LENGTH_LONG).show();
return;
}
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == BaseActivity.RESULT_OK && data != null) {
Intent i = new Intent(activity, FlashActivity.class)
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
activity.startActivity(i);
}
});
patchBoot(activity);
break;
case 0:
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
Download.receive(activity, new DownloadReceiver() {
@Override
public void onDownloadDone(Context context, Uri uri) {
SnackbarMaker.showUri(activity, uri);
}
}, Data.magiskLink, filename);
downloadOnly(activity);
break;
case 2:
intent = new Intent(activity, FlashActivity.class)
intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
activity.startActivity(intent);
break;
case 3:
new CustomAlertDialog(activity)
.setTitle(R.string.warning)
.setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> {
Intent it = new Intent(activity, FlashActivity.class)
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(it);
})
.setNegativeButton(R.string.no_thanks, null)
.show();
installInactiveSlot(activity);
break;
default:
}
});
}
private void patchBoot(BaseActivity a) {
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
a.runWithPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, () ->
a.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == Activity.RESULT_OK && data != null) {
Intent i = new Intent(a, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
a.startActivity(i);
}
})
);
}
private void downloadOnly(BaseActivity a) {
a.runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
ProgressNotification progress = new ProgressNotification(filename);
AndroidNetworking
.download(Data.magiskLink, Const.EXTERNAL_PATH.getPath(), filename)
.build()
.setDownloadProgressListener(progress)
.startDownload(new DownloadListener() {
@Override
public void onDownloadComplete() {
progress.dlDone();
SnackbarMaker.make(a,
a.getString(R.string.internal_storage, "/Download/" + filename),
Snackbar.LENGTH_LONG).show();
}
@Override
public void onError(ANError anError) {
progress.dlFail();
}
});
});
}
private void installInactiveSlot(BaseActivity activity) {
new CustomAlertDialog(activity)
.setTitle(R.string.warning)
.setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> {
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(intent);
})
.setNegativeButton(R.string.no_thanks, null)
.show();
}
}

View File

@@ -1,36 +1,28 @@
package com.topjohnwu.magisk.components;
import android.Manifest;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.receivers.ManagerUpdate;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.NonNull;
public class ManagerInstallDialog extends CustomAlertDialog {
public ManagerInstallDialog(@NonNull BaseActivity activity) {
super(activity);
MagiskManager mm = Data.MM();
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
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, name));
setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> activity.runWithPermission(
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(mm, ManagerUpdate.class);
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
mm.sendBroadcast(intent);
}))
.setNegativeButton(R.string.no_thanks, null);
setPositiveButton(R.string.install, (d, i) -> DlInstallManager.upgrade(name));
setNegativeButton(R.string.no_thanks, null);
if (!TextUtils.isEmpty(Data.managerNoteLink)) {
setNeutralButton(R.string.app_changelog, (d, i) ->
new MarkDownWindow(activity, null, Data.managerNoteLink).exec());

View File

@@ -0,0 +1,114 @@
package com.topjohnwu.magisk.components;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.utils.Utils;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.TaskStackBuilder;
public class Notifications {
public static void setup(Context c) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager mgr = c.getSystemService(NotificationManager.class);
mgr.deleteNotificationChannel("magisk_notification");
NotificationChannel channel =
new NotificationChannel(Const.ID.UPDATE_NOTIFICATION_CHANNEL,
c.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT);
mgr.createNotificationChannel(channel);
channel = new NotificationChannel(Const.ID.PROGRESS_NOTIFICATION_CHANNEL,
c.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW);
mgr.createNotificationChannel(channel);
}
}
public static void magiskUpdate() {
MagiskManager mm = Data.MM();
Intent intent = new Intent(mm, Data.classMap.get(SplashActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
stackBuilder.addParentStack(Data.classMap.get(SplashActivity.class));
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.magisk_update_title))
.setContentText(mm.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void managerUpdate() {
MagiskManager mm = Data.MM();
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class));
intent.setAction(Const.Key.BROADCAST_MANAGER_UPDATE);
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
intent.putExtra(Const.Key.INTENT_SET_NAME, name);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.manager_update_title))
.setContentText(mm.getString(R.string.manager_download_install))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
mgr.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void dtboPatched() {
MagiskManager mm = Data.MM();
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class))
.setAction(Const.Key.BROADCAST_REBOOT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
.setVibrate(new long[]{0, 100, 100, 100})
.addAction(R.drawable.ic_refresh, mm.getString(R.string.reboot), pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
}
public static NotificationCompat.Builder progress(String title) {
MagiskManager mm = Data.MM();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.PROGRESS_NOTIFICATION_CHANNEL);
builder.setPriority(NotificationCompat.PRIORITY_LOW)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(title)
.setProgress(0, 0, true)
.setOngoing(true);
return builder;
}
}

View File

@@ -0,0 +1,68 @@
package com.topjohnwu.magisk.components;
import android.widget.Toast;
import com.androidnetworking.interfaces.DownloadProgressListener;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
public class ProgressNotification implements DownloadProgressListener {
private NotificationManagerCompat mgr;
private NotificationCompat.Builder builder;
private long prevTime;
public ProgressNotification(String title) {
MagiskManager mm = Data.MM();
mgr = NotificationManagerCompat.from(mm);
builder = Notifications.progress(title);
prevTime = System.currentTimeMillis();
update();
Utils.toast(mm.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
}
@Override
public void onProgress(long bytesDownloaded, long totalBytes) {
long cur = System.currentTimeMillis();
if (cur - prevTime >= 1000) {
prevTime = cur;
int progress = (int) (bytesDownloaded * 100 / totalBytes);
builder.setProgress(100, progress, false);
builder.setContentText(progress + "%");
update();
}
}
public NotificationCompat.Builder getNotification() {
return builder;
}
public void update() {
mgr.notify(hashCode(), builder.build());
}
public void dlDone() {
builder.setProgress(0, 0, false)
.setContentText(Data.MM().getString(R.string.download_complete))
.setSmallIcon(R.drawable.ic_check_circle)
.setOngoing(false);
update();
}
public void dlFail() {
builder.setProgress(0, 0, false)
.setContentText(Data.MM().getString(R.string.download_file_error))
.setSmallIcon(R.drawable.ic_cancel)
.setOngoing(false);
update();
}
public void dismiss() {
mgr.cancel(hashCode());
}
}

View File

@@ -2,14 +2,15 @@ package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.net.Uri;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.StringRes;
public class SnackbarMaker {
public static Snackbar make(Activity activity, CharSequence text, int duration) {
@@ -34,7 +35,7 @@ public class SnackbarMaker {
}
private static void setup(Snackbar snack) {
TextView text = snack.getView().findViewById(android.support.design.R.id.snackbar_text);
TextView text = snack.getView().findViewById(com.google.android.material.R.id.snackbar_text);
text.setMaxLines(Integer.MAX_VALUE);
}

View File

@@ -2,28 +2,29 @@ package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.DownloadListener;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import androidx.annotation.NonNull;
public class UninstallDialog extends CustomAlertDialog {
public UninstallDialog(@NonNull Activity activity) {
super(activity);
MagiskManager mm = Data.MM();
setTitle(R.string.uninstall_magisk_title);
setMessage(R.string.uninstall_magisk_msg);
setNeutralButton(R.string.restore_img, (d, i) -> {
@@ -40,17 +41,29 @@ public class UninstallDialog extends CustomAlertDialog {
});
});
if (!TextUtils.isEmpty(Data.uninstallerLink)) {
setPositiveButton(R.string.complete_uninstall, (d, i) ->
Download.receive(activity, new DownloadReceiver() {
setPositiveButton(R.string.complete_uninstall, (d, i) -> {
File zip = new File(activity.getFilesDir(), "uninstaller.zip");
ProgressNotification progress = new ProgressNotification(zip.getName());
AndroidNetworking.download(Data.uninstallerLink, zip.getParent(), zip.getName())
.build()
.setDownloadProgressListener(progress)
.startDownload(new DownloadListener() {
@Override
public void onDownloadDone(Context context, Uri uri) {
Intent intent = new Intent(context, FlashActivity.class)
public void onDownloadComplete() {
progress.dismiss();
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(uri)
.setData(Uri.fromFile(zip))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
context.startActivity(intent);
activity.startActivity(intent);
}
}, Data.uninstallerLink, "magisk-uninstaller.zip"));
@Override
public void onError(ANError anError) {
progress.dlFail();
}
});
});
}
}
}

View File

@@ -1,29 +1,35 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;
import java.util.List;
import androidx.annotation.NonNull;
public abstract class BaseModule implements Comparable<BaseModule> {
private String mId = null, mName, mVersion, mAuthor, mDescription;
private String mId, mName, mVersion, mAuthor, mDescription;
private int mVersionCode = -1, minMagiskVersion = -1;
protected BaseModule() {}
protected BaseModule() {
mId = mName = mVersion = mAuthor = mDescription = "";
}
protected BaseModule(Cursor c) {
mId = c.getString(c.getColumnIndex("id"));
mName = c.getString(c.getColumnIndex("name"));
mVersion = c.getString(c.getColumnIndex("version"));
mId = nonNull(c.getString(c.getColumnIndex("id")));
mName = nonNull(c.getString(c.getColumnIndex("name")));
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
mAuthor = c.getString(c.getColumnIndex("author"));
mDescription = c.getString(c.getColumnIndex("description"));
mAuthor = nonNull(c.getString(c.getColumnIndex("author")));
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk"));
}
private String nonNull(String s) {
return s == null ? "" : s;
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("id", mId);

View File

@@ -11,19 +11,19 @@ public class Module extends BaseModule {
public Module(String path) {
try {
parseProps(Shell.Sync.su("dos2unix < " + path + "/module.prop"));
parseProps(Shell.su("dos2unix < " + path + "/module.prop").exec().getOut());
} catch (NumberFormatException ignored) {}
mRemoveFile = new SuFile(path, "remove");
mDisableFile = new SuFile(path, "disable");
mUpdateFile = new SuFile(path, "update");
if (getId() == null) {
if (getId().isEmpty()) {
int sep = path.lastIndexOf('/');
setId(path.substring(sep + 1));
}
if (getName() == null) {
if (getName().isEmpty()) {
setName(getId());
}
@@ -33,13 +33,11 @@ public class Module extends BaseModule {
}
public void createDisableFile() {
mEnable = false;
mDisableFile.createNewFile();
mEnable = !mDisableFile.createNewFile();
}
public void removeDisableFile() {
mEnable = true;
mDisableFile.delete();
mEnable = mDisableFile.delete();
}
public boolean isEnabled() {
@@ -47,13 +45,11 @@ public class Module extends BaseModule {
}
public void createRemoveFile() {
mRemove = true;
mRemoveFile.createNewFile();
mRemove = mRemoveFile.createNewFile();
}
public void deleteRemoveFile() {
mRemove = false;
mRemoveFile.delete();
mRemove = !mRemoveFile.delete();
}
public boolean willBeRemoved() {

View File

@@ -3,8 +3,10 @@ package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.NonNull;
public class Policy implements Comparable<Policy>{
@@ -25,22 +27,22 @@ public class Policy implements Comparable<Policy>{
this.uid = uid;
packageName = pkgs[0];
info = pm.getApplicationInfo(packageName, 0);
appName = info.loadLabel(pm).toString();
appName = Utils.getAppLabel(info, pm);
}
public Policy(Cursor c, PackageManager pm) throws PackageManager.NameNotFoundException {
uid = c.getInt(c.getColumnIndex("uid"));
packageName = c.getString(c.getColumnIndex("package_name"));
policy = c.getInt(c.getColumnIndex("policy"));
until = c.getLong(c.getColumnIndex("until"));
logging = c.getInt(c.getColumnIndex("logging")) != 0;
notification = c.getInt(c.getColumnIndex("notification")) != 0;
public Policy(ContentValues values, PackageManager pm) throws PackageManager.NameNotFoundException {
uid = values.getAsInteger("uid");
packageName = values.getAsString("package_name");
policy = values.getAsInteger("policy");
until = values.getAsInteger("until");
logging = values.getAsInteger("logging") != 0;
notification = values.getAsInteger("notification") != 0;
info = pm.getApplicationInfo(packageName, 0);
if (info.uid != uid)
throw new PackageManager.NameNotFoundException();
appName = info.loadLabel(pm).toString();
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("uid", uid);

View File

@@ -7,42 +7,36 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import java.text.DateFormat;
import java.util.Date;
public class Repo extends BaseModule {
private String repoName;
private Date mLastUpdate;
public Repo(String name) {
repoName = name;
public Repo(String id) {
setId(id);
}
public Repo(Cursor c) {
super(c);
repoName = c.getString(c.getColumnIndex("repo_name"));
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
}
public void update() throws IllegalRepoException {
String props[] = Utils.dos2unix(WebService.getString(getManifestUrl())).split("\\n");
String props[] = Utils.dlString(getPropUrl()).split("\\n");
try {
parseProps(props);
} catch (NumberFormatException e) {
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
throw new IllegalRepoException("Repo [" + getId() + "] parse error: " + e.getMessage());
}
if (getId() == null) {
throw new IllegalRepoException("Repo [" + repoName + "] does not contain id");
}
if (getVersionCode() < 0) {
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
throw new IllegalRepoException("Repo [" + getId() + "] does not contain versionCode");
}
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
Logger.debug("Repo [" + repoName + "] is outdated");
if (getMinMagiskVersion() < Const.MIN_MODULE_VER) {
Logger.debug("Repo [" + getId() + "] is outdated");
}
}
@@ -54,25 +48,20 @@ public class Repo extends BaseModule {
@Override
public ContentValues getContentValues() {
ContentValues values = super.getContentValues();
values.put("repo_name", repoName);
values.put("last_update", mLastUpdate.getTime());
return values;
}
public String getRepoName() {
return repoName;
}
public String getZipUrl() {
return String.format(Const.Url.ZIP_URL, repoName);
return String.format(Const.Url.ZIP_URL, getId());
}
public String getManifestUrl() {
return String.format(Const.Url.FILE_URL, repoName, "module.prop");
public String getPropUrl() {
return String.format(Const.Url.FILE_URL, getId(), "module.prop");
}
public String getDetailUrl() {
return String.format(Const.Url.FILE_URL, repoName, "README.md");
return String.format(Const.Url.FILE_URL, getId(), "README.md");
}
public String getLastUpdateString() {

View File

@@ -1,7 +1,6 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
import com.topjohnwu.magisk.utils.LocaleManager;
@@ -20,17 +19,18 @@ public class SuLogEntry {
fromUid = policy.uid;
packageName = policy.packageName;
appName = policy.appName;
action = policy.policy == Policy.ALLOW;
}
public SuLogEntry(Cursor c) {
fromUid = c.getInt(c.getColumnIndex("from_uid"));
fromPid = c.getInt(c.getColumnIndex("from_pid"));
toUid = c.getInt(c.getColumnIndex("to_uid"));
packageName = c.getString(c.getColumnIndex("package_name"));
appName = c.getString(c.getColumnIndex("app_name"));
command = c.getString(c.getColumnIndex("command"));
action = c.getInt(c.getColumnIndex("action")) != 0;
date = new Date(c.getLong(c.getColumnIndex("time")));
public SuLogEntry(ContentValues values) {
fromUid = values.getAsInteger("from_uid");
packageName = values.getAsString("package_name");
appName = values.getAsString("app_name");
fromPid = values.getAsInteger("from_pid");
command = values.getAsString("command");
toUid = values.getAsInteger("to_uid");
action = values.getAsInteger("action") != 0;
date = new Date(values.getAsLong("time"));
}
public ContentValues getContentValues() {

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.container;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -9,6 +7,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
public class ValueSortedMap<K, V extends Comparable<? super V>> extends HashMap<K, V> {
private List<V> sorted = new ArrayList<>();

View File

@@ -0,0 +1,66 @@
package com.topjohnwu.magisk.database;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import java.util.Collections;
import java.util.List;
import androidx.annotation.NonNull;
public class MagiskDB {
static final String POLICY_TABLE = "policies";
static final String LOG_TABLE = "logs";
static final String SETTINGS_TABLE = "settings";
static final String STRINGS_TABLE = "strings";
static final File LEGACY_MANAGER_DB =
new File(Utils.fmt("/sbin/.magisk/db-%d/magisk.db", Const.USER_ID));
@NonNull
public static MagiskDB getInstance() {
if (LEGACY_MANAGER_DB.canWrite()) {
return MagiskDBLegacy.newInstance();
} else if (Shell.rootAccess()) {
return Data.magiskVersionCode >= Const.MAGISK_VER.CMDLINE_DB ?
new MagiskDBCmdline() : MagiskDBLegacy.newInstance();
} else {
return new MagiskDB();
}
}
public void clearOutdated() {}
public void deletePolicy(Policy policy) {
deletePolicy(policy.uid);
}
public void deletePolicy(String pkg) {}
public void deletePolicy(int uid) {}
public Policy getPolicy(int uid) { return null; }
public void updatePolicy(Policy policy) {}
public List<Policy> getPolicyList() { return Collections.emptyList(); }
public List<List<SuLogEntry>> getLogs() { return Collections.emptyList(); }
public void addLog(SuLogEntry log) {}
public void clearLogs() {}
public void setSettings(String key, int value) {}
public int getSettings(String key, int defaultValue) { return defaultValue; }
public void setStrings(String key, String value) {}
public String getStrings(String key, String defaultValue) { return defaultValue; }
}

View File

@@ -0,0 +1,189 @@
package com.topjohnwu.magisk.database;
import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class MagiskDBCmdline extends MagiskDB {
private PackageManager pm;
public MagiskDBCmdline() {
pm = Data.MM().getPackageManager();
}
private List<String> rawSQL(String fmt, Object... args) {
return Shell.su("magisk --sqlite '" + Utils.fmt(fmt, args) + "'").exec().getOut();
}
private List<ContentValues> SQL(String fmt, Object... args) {
List<ContentValues> list = new ArrayList<>();
for (String raw : rawSQL(fmt, args)) {
ContentValues values = new ContentValues();
String[] cols = raw.split("\\|");
for (String col : cols) {
String[] pair = col.split("=", 2);
if (pair.length != 2)
continue;
values.put(pair[0], pair[1]);
}
list.add(values);
}
return list;
}
private String toSQL(ContentValues values) {
StringBuilder keys = new StringBuilder(), vals = new StringBuilder();
keys.append('(');
vals.append("VALUES(");
boolean first = true;
for (Map.Entry<String, Object> entry : values.valueSet()) {
if (!first) {
keys.append(',');
vals.append(',');
} else {
first = false;
}
keys.append(entry.getKey());
vals.append('"');
vals.append(entry.getValue());
vals.append('"');
}
keys.append(')');
vals.append(')');
keys.append(vals);
return keys.toString();
}
@Override
public void clearOutdated() {
rawSQL(
"DELETE FROM %s WHERE until > 0 AND until < %d;" +
"DELETE FROM %s WHERE time < %d",
POLICY_TABLE, System.currentTimeMillis() / 1000,
LOG_TABLE, System.currentTimeMillis() - Data.suLogTimeout * 86400000
);
}
@Override
public void deletePolicy(String pkg) {
rawSQL("DELETE FROM %s WHERE package_name=\"%s\"", POLICY_TABLE, pkg);
}
@Override
public void deletePolicy(int uid) {
rawSQL("DELETE FROM %s WHERE uid=%d", POLICY_TABLE, uid);
}
@Override
public Policy getPolicy(int uid) {
List<ContentValues> res =
SQL("SELECT * FROM %s WHERE uid=%d", POLICY_TABLE, uid);
if (!res.isEmpty()) {
try {
return new Policy(res.get(0), pm);
} catch (PackageManager.NameNotFoundException e) {
deletePolicy(uid);
}
}
return null;
}
@Override
public void updatePolicy(Policy policy) {
rawSQL("REPLACE INTO %s %s", POLICY_TABLE, toSQL(policy.getContentValues()));
}
@Override
public List<Policy> getPolicyList() {
List<Policy> list = new ArrayList<>();
for (ContentValues values : SQL("SELECT * FROM %s WHERE uid/100000=%d", POLICY_TABLE, Const.USER_ID)) {
try {
list.add(new Policy(values, pm));
} catch (PackageManager.NameNotFoundException e) {
deletePolicy(values.getAsInteger("uid"));
}
}
Collections.sort(list);
return list;
}
@Override
public List<List<SuLogEntry>> getLogs() {
List<List<SuLogEntry>> ret = new ArrayList<>();
List<SuLogEntry> list = null;
String dateString = null, newString;
for (ContentValues values : SQL("SELECT * FROM %s ORDER BY time DESC", LOG_TABLE)) {
Date date = new Date(values.getAsLong("time"));
newString = DateFormat.getDateInstance(DateFormat.MEDIUM, LocaleManager.locale).format(date);
if (!TextUtils.equals(dateString, newString)) {
dateString = newString;
list = new ArrayList<>();
ret.add(list);
}
list.add(new SuLogEntry(values));
}
return ret;
}
@Override
public void addLog(SuLogEntry log) {
rawSQL("INSERT INTO %s %s", LOG_TABLE, toSQL(log.getContentValues()));
}
@Override
public void clearLogs() {
rawSQL("DELETE FROM %s", LOG_TABLE);
}
@Override
public void setSettings(String key, int value) {
ContentValues data = new ContentValues();
data.put("key", key);
data.put("value", value);
rawSQL("REPLACE INTO %s %s", SETTINGS_TABLE, toSQL(data));
}
@Override
public int getSettings(String key, int defaultValue) {
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", SETTINGS_TABLE, key);
if (res.isEmpty())
return defaultValue;
return res.get(0).getAsInteger("value");
}
@Override
public void setStrings(String key, String value) {
if (value == null) {
rawSQL("DELETE FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
return;
}
ContentValues data = new ContentValues();
data.put("key", key);
data.put("value", value);
rawSQL("REPLACE INTO %s %s", STRINGS_TABLE, toSQL(data));
}
@Override
public String getStrings(String key, String defaultValue) {
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
if (res.isEmpty())
return defaultValue;
return res.get(0).getAsString("value");
}
}

View File

@@ -4,10 +4,10 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Process;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.widget.Toast;
@@ -22,41 +22,33 @@ import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class MagiskDatabaseHelper {
public class MagiskDBLegacy extends MagiskDB {
private static final int DATABASE_VER = 6;
private static final int OLD_DATABASE_VER = 5;
private static final String POLICY_TABLE = "policies";
private static final String LOG_TABLE = "logs";
private static final String SETTINGS_TABLE = "settings";
private static final String STRINGS_TABLE = "strings";
private PackageManager pm;
private SQLiteDatabase db;
private MagiskManager mm;
@NonNull
public static MagiskDatabaseHelper getInstance(MagiskManager mm) {
static MagiskDBLegacy newInstance() {
try {
return new MagiskDatabaseHelper(mm);
return new MagiskDBLegacy();
} catch (Exception e) {
// Let's cleanup everything and try again
Shell.su("db_clean '*'").exec();
return new MagiskDatabaseHelper(mm);
return new MagiskDBLegacy();
}
}
private MagiskDatabaseHelper(MagiskManager context) {
mm = context;
pm = mm.getPackageManager();
db = openDatabase(mm);
private MagiskDBLegacy() {
pm = Data.MM().getPackageManager();
db = openDatabase();
db.disableWriteAheadLogging();
int version = Data.magiskVersionCode >= Const.MAGISK_VER.DBVER_SIX ? DATABASE_VER : OLD_DATABASE_VER;
int curVersion = db.getVersion();
@@ -70,48 +62,37 @@ public class MagiskDatabaseHelper {
clearOutdated();
}
private SQLiteDatabase openDatabase(MagiskManager mm) {
final File DB_FILE = new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
private SQLiteDatabase openDatabase() {
MagiskManager mm = Data.MM();
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? mm.createDeviceProtectedStorageContext() : mm;
if (!DB_FILE.canWrite()) {
if (!Shell.rootAccess()) {
if (!LEGACY_MANAGER_DB.canWrite()) {
if (!Shell.rootAccess() || Data.magiskVersionCode < 0) {
// We don't want the app to crash, create a db and return
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
}
// Cleanup
Shell.su("db_clean " + Const.USER_ID).exec();
if (Data.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
// Super old legacy mode
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
} else if (Data.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
// Legacy mode with FBE aware
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
de.moveDatabaseFrom(mm, "su.db");
}
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
} else {
// Global database
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
mm.deleteDatabase("su.db");
de.deleteDatabase("su.db");
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
// We need some additional policies on old versions
Shell.su("db_sepatch").exec();
}
if (!GLOBAL_DB.exists()) {
Shell.su("db_init").exec();
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
Shell.su("db_restore").exec();
}
// Global database
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
mm.deleteDatabase("su.db");
de.deleteDatabase("su.db");
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
// We need some additional policies on old versions
Shell.su("db_sepatch").exec();
}
if (!GLOBAL_DB.exists()) {
Shell.su("db_init").exec();
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
Shell.su("db_restore").exec();
}
Shell.su("db_setup " + Process.myUid()).exec();
}
// Not using legacy mode, open the mounted global DB
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
return SQLiteDatabase.openOrCreateDatabase(LEGACY_MANAGER_DB, null);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion) {
private void onUpgrade(SQLiteDatabase db, int oldVersion) {
if (oldVersion == 0) {
createTables(db);
oldVersion = 3;
@@ -146,13 +127,13 @@ public class MagiskDatabaseHelper {
}
if (oldVersion == 5) {
setSettings(Const.Key.SU_FINGERPRINT,
mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
Data.MM().prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
++oldVersion;
}
}
// Remove everything, we do not support downgrade
public void onDowngrade(SQLiteDatabase db) {
private void onDowngrade(SQLiteDatabase db) {
Utils.toast(R.string.su_db_corrupt, Toast.LENGTH_LONG);
db.execSQL("DROP TABLE IF EXISTS " + POLICY_TABLE);
db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE);
@@ -165,22 +146,23 @@ public class MagiskDatabaseHelper {
// Policies
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + POLICY_TABLE + " " +
"(uid INT, package_name TEXT, policy INT, " +
"until INT, logging INT, notification INT, " +
"PRIMARY KEY(uid))");
"(uid INT, package_name TEXT, policy INT, " +
"until INT, logging INT, notification INT, " +
"PRIMARY KEY(uid))");
// Logs
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + LOG_TABLE + " " +
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
"to_uid INT, action INT, time INT, command TEXT)");
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
"to_uid INT, action INT, time INT, command TEXT)");
// Settings
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + SETTINGS_TABLE + " " +
"(key TEXT, value INT, PRIMARY KEY(key))");
"(key TEXT, value INT, PRIMARY KEY(key))");
}
@Override
public void clearOutdated() {
// Clear outdated policies
db.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);
@@ -188,23 +170,24 @@ public class MagiskDatabaseHelper {
db.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - Data.suLogTimeout * 86400000), null);
}
public void deletePolicy(Policy policy) {
deletePolicy(policy.uid);
}
@Override
public void deletePolicy(String pkg) {
db.delete(POLICY_TABLE, "package_name=?", new String[] { pkg });
}
@Override
public void deletePolicy(int uid) {
db.delete(POLICY_TABLE, Utils.fmt("uid=%d", uid), null);
}
@Override
public Policy getPolicy(int uid) {
Policy policy = null;
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid=%d", uid), null, null, null, null)) {
if (c.moveToNext()) {
policy = new Policy(c, pm);
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(c, values);
policy = new Policy(values, pm);
}
} catch (PackageManager.NameNotFoundException e) {
deletePolicy(uid);
@@ -213,21 +196,21 @@ public class MagiskDatabaseHelper {
return policy;
}
public void addPolicy(Policy policy) {
@Override
public void updatePolicy(Policy policy) {
db.replace(POLICY_TABLE, null, policy.getContentValues());
}
public void updatePolicy(Policy policy) {
db.update(POLICY_TABLE, policy.getContentValues(), Utils.fmt("uid=%d", policy.uid), null);
}
public List<Policy> getPolicyList(PackageManager pm) {
@Override
public List<Policy> getPolicyList() {
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid/100000=%d", Const.USER_ID),
null, null, null, null)) {
List<Policy> ret = new ArrayList<>(c.getCount());
while (c.moveToNext()) {
try {
Policy policy = new Policy(c, pm);
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(c, values);
Policy policy = new Policy(values, pm);
ret.add(policy);
} catch (PackageManager.NameNotFoundException e) {
// The app no longer exist, remove from DB
@@ -239,11 +222,12 @@ public class MagiskDatabaseHelper {
}
}
public List<List<Integer>> getLogStructure() {
try (Cursor c = db.query(LOG_TABLE, new String[] { "time" }, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
@Override
public List<List<SuLogEntry>> getLogs() {
try (Cursor c = db.query(LOG_TABLE, null, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
null, null, null, "time DESC")) {
List<List<Integer>> ret = new ArrayList<>();
List<Integer> list = null;
List<List<SuLogEntry>> ret = new ArrayList<>();
List<SuLogEntry> list = null;
String dateString = null, newString;
while (c.moveToNext()) {
Date date = new Date(c.getLong(c.getColumnIndex("time")));
@@ -253,25 +237,25 @@ public class MagiskDatabaseHelper {
list = new ArrayList<>();
ret.add(list);
}
list.add(c.getPosition());
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(c, values);
list.add(new SuLogEntry(values));
}
return ret;
}
}
public Cursor getLogCursor() {
return db.query(LOG_TABLE, null, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
null, null, null, "time DESC");
}
@Override
public void addLog(SuLogEntry log) {
db.insert(LOG_TABLE, null, log.getContentValues());
}
@Override
public void clearLogs() {
db.delete(LOG_TABLE, null, null);
}
@Override
public void setSettings(String key, int value) {
ContentValues data = new ContentValues();
data.put("key", key);
@@ -279,6 +263,7 @@ public class MagiskDatabaseHelper {
db.replace(SETTINGS_TABLE, null, data);
}
@Override
public int getSettings(String key, int defaultValue) {
int value = defaultValue;
try (Cursor c = db.query(SETTINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
@@ -289,6 +274,7 @@ public class MagiskDatabaseHelper {
return value;
}
@Override
public void setStrings(String key, String value) {
if (value == null) {
db.delete(STRINGS_TABLE, "key=?", new String[] { key });
@@ -300,6 +286,7 @@ public class MagiskDatabaseHelper {
}
}
@Override
public String getStrings(String key, String defaultValue) {
String value = defaultValue;
try (Cursor c = db.query(STRINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {

View File

@@ -16,7 +16,7 @@ import java.util.Set;
public class RepoDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VER = 3;
private static final int DATABASE_VER = 4;
private static final String TABLE_NAME = "repos";
private SQLiteDatabase mDb;
@@ -29,8 +29,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
mDb = getWritableDatabase();
// Remove outdated repos
mDb.delete(TABLE_NAME, "minMagisk<?",
new String[] { String.valueOf(Const.MIN_MODULE_VER()) });
mDb.delete(TABLE_NAME, "minMagisk<?", new String[] { String.valueOf(Const.MIN_MODULE_VER) });
}
@Override
@@ -40,21 +39,14 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
try {
if (oldVersion < 3) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, repo_name TEXT, last_update INT, " +
"PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
oldVersion = 3;
}
} catch (Exception e) {
e.printStackTrace();
// Reset database
onDowngrade(db, DATABASE_VER, 0);
if (oldVersion != newVersion) {
// Nuke old DB and create new table
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
}
}
@@ -75,8 +67,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
}
public void removeRepo(Repo repo) {
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
notifyAdapter();
removeRepo(repo.getId());
}
public void removeRepo(Iterable<String> list) {
@@ -115,7 +106,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
orderBy = "last_update DESC";
}
return mDb.query(TABLE_NAME, null, "minMagisk<=? AND minMagisk>=?",
new String[] { String.valueOf(Data.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER()) },
new String[] { String.valueOf(Data.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER) },
null, null, orderBy);
}

View File

@@ -2,27 +2,21 @@ package com.topjohnwu.magisk.fragments;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.google.android.material.tabs.TabLayout;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class LogFragment extends BaseFragment {
private Unbinder unbinder;
@BindView(R.id.container) ViewPager viewPager;
@BindView(R.id.tab) TabLayout tab;
@@ -31,15 +25,13 @@ public class LogFragment extends BaseFragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_log, container, false);
unbinder = ButterKnife.bind(this, v);
unbinder = new LogFragment_ViewBinding(this, v);
((MainActivity) requireActivity()).toolbar.setElevation(0);
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
if (!(Const.USER_ID > 0 && Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
}
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
tab.setupWithViewPager(viewPager);
tab.setVisibility(View.VISIBLE);
@@ -48,11 +40,4 @@ public class LogFragment extends BaseFragment {
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -5,11 +5,6 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,20 +36,22 @@ import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.cardview.widget.CardView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class MagiskFragment extends BaseFragment
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
private Container expandableContainer = new Container();
private Unbinder unbinder;
private static boolean shownDialog = false;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.swipeRefreshLayout) public SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.core_only_notice) CardView coreOnlyNotice;
@@ -85,7 +82,7 @@ public class MagiskFragment extends BaseFragment
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.grey500) int colorNeutral;
@BindColor(R.color.green500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
@OnClick(R.id.safetyNet_title)
@@ -136,7 +133,7 @@ public class MagiskFragment extends BaseFragment
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
unbinder = ButterKnife.bind(this, v);
unbinder = new MagiskFragment_ViewBinding(this, v);
requireActivity().setTitle(R.string.magisk);
expandableContainer.expandLayout = expandLayout;
@@ -196,12 +193,6 @@ public class MagiskFragment extends BaseFragment
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public Container getContainer() {
return expandableContainer;
@@ -223,11 +214,10 @@ public class MagiskFragment extends BaseFragment
boolean hasNetwork = Download.checkNetworkStatus(mm);
boolean hasRoot = Shell.rootAccess();
boolean isUpToDate = Data.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(isUpToDate && hasRoot ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(hasRoot ? View.VISIBLE : View.GONE);
coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
int image, color;

View File

@@ -1,10 +1,6 @@
package com.topjohnwu.magisk.fragments;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -17,13 +13,14 @@ import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.utils.Topic;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
SearchView search;
@@ -42,7 +39,7 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new MagiskHideFragment_ViewBinding(this, view);
appAdapter = new ApplicationAdapter(requireActivity());
recyclerView.setAdapter(appAdapter);
@@ -76,12 +73,6 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
search.setOnQueryTextListener(searchListener);
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public int[] getSubscribedTopics() {
return new int[] {Topic.MAGISK_HIDE_DONE};

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.Manifest;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -16,11 +14,11 @@ import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
@@ -28,14 +26,11 @@ import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import androidx.annotation.Nullable;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskLogFragment extends BaseFragment {
private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@@ -45,7 +40,7 @@ public class MagiskLogFragment extends BaseFragment {
@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);
unbinder = new MagiskLogFragment_ViewBinding(this, view);
setHasOptionsMenu(true);
txtLog.setTextIsSelectable(true);
return view;
@@ -63,12 +58,6 @@ public class MagiskLogFragment extends BaseFragment {
readLogs();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
@@ -91,7 +80,7 @@ public class MagiskLogFragment extends BaseFragment {
}
}
public void readLogs() {
private void readLogs() {
Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
progressBar.setVisibility(View.GONE);
if (result.getOut().isEmpty())
@@ -103,14 +92,14 @@ public class MagiskLogFragment extends BaseFragment {
});
}
public void saveLogs() {
private 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 logFile = new File(Download.EXTERNAL_PATH, filename);
File logFile = new File(Const.EXTERNAL_PATH, filename);
try {
logFile.createNewFile();
} catch (IOException e) {
@@ -121,7 +110,7 @@ public class MagiskLogFragment extends BaseFragment {
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
}
public void clearLogs() {
private void clearLogs() {
Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
txtLog.setText(R.string.log_is_empty);
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();

View File

@@ -4,10 +4,6 @@ import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -17,6 +13,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
@@ -30,19 +27,21 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@OnClick(R.id.fab)
public void selectFile() {
void selectFile() {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip");
@@ -56,7 +55,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_modules, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new ModulesFragment_ViewBinding(this, view);
setHasOptionsMenu(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> {
@@ -95,18 +94,12 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
Intent intent = new Intent(getActivity(), FlashActivity.class);
Intent intent = new Intent(getActivity(), Data.classMap.get(FlashActivity.class));
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
startActivity(intent);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_reboot, menu);
@@ -125,7 +118,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
Shell.su("/system/bin/reboot bootloader").submit();
return true;
case R.id.reboot_download:
Shell.su("/system/bin/reboot download").submit();
Shell.su("/system/bin/reboot upgrade").submit();
return true;
default:
return false;

View File

@@ -2,10 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -26,13 +22,14 @@ import com.topjohnwu.magisk.utils.Topic;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class ReposFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@@ -49,7 +46,7 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_repos, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new ReposFragment_ViewBinding(this, view);
mSwipeRefreshLayout.setRefreshing(true);
recyclerView.setVisibility(View.GONE);
@@ -123,6 +120,5 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
public void onDestroyView() {
super.onDestroyView();
mm.repoDB.unregisterAdapter();
unbinder.unbind();
}
}

View File

@@ -1,85 +1,110 @@
package com.topjohnwu.magisk.fragments;
import android.content.Context;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
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.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.IOException;
import java.util.Locale;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.RecyclerView;
public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener,
Topic.Subscriber, Topic.AutoSubscriber {
private PreferenceScreen prefScreen;
private ListPreference updateChannel, suAccess, autoRes, suNotification,
requestTimeout, multiuserMode, namespaceMode;
private MagiskManager mm;
private PreferenceCategory generalCatagory;
private ListPreference updateChannel, autoRes, suNotification,
requestTimeout, rootConfig, multiuserConfig, nsConfig;
private int rootState, namespaceState;
private boolean showSuperuser;
private void prefsSync() {
rootState = mm.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
namespaceState = mm.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
showSuperuser = Utils.showSuperUser();
mm.prefs.edit()
.putString(Const.Key.ROOT_ACCESS, String.valueOf(rootState))
.putString(Const.Key.SU_MNT_NS, String.valueOf(namespaceState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(Data.multiuserState))
.putBoolean(Const.Key.SU_FINGERPRINT, FingerprintHelper.useFingerPrint())
.apply();
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.app_settings, rootKey);
mm = Data.MM();
prefScreen = getPreferenceScreen();
prefsSync();
generalCatagory = (PreferenceCategory) findPreference("general");
setPreferencesFromResource(R.xml.app_settings, rootKey);
PreferenceScreen prefScreen = getPreferenceScreen();
PreferenceCategory generalCatagory = (PreferenceCategory) findPreference("general");
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
Preference hideManager = findPreference("hide");
hideManager.setOnPreferenceClickListener(pref -> {
PatchAPK.hideManager();
return true;
});
Preference restoreManager = findPreference("restore");
findPreference("clear").setOnPreferenceClickListener((pref) -> {
restoreManager.setOnPreferenceClickListener(pref -> {
DlInstallManager.restore();
return true;
});
findPreference("clear").setOnPreferenceClickListener(pref -> {
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
mm.repoDB.clearRepo();
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
return true;
});
findPreference("hosts").setOnPreferenceClickListener(pref -> {
Shell.su("add_hosts_module").exec();
Utils.loadModules();
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT);
return true;
});
updateChannel = (ListPreference) findPreference(Const.Key.UPDATE_CHANNEL);
suAccess = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
rootConfig = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
multiuserConfig = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
nsConfig = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
@@ -109,7 +134,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
// Disable dangerous settings in secondary user
if (Const.USER_ID > 0) {
suCategory.removePreference(multiuserMode);
suCategory.removePreference(multiuserConfig);
}
// Disable re-authentication option on Android O, it will not work
@@ -126,38 +151,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
fingerprint.setSummary(R.string.disable_fingerprint);
}
if (Data.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
hideManager.setOnPreferenceClickListener((pref) -> {
PatchAPK.hideManager(requireActivity());
return true;
});
if (Shell.rootAccess() && Const.USER_ID == 0) {
if (mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
generalCatagory.removePreference(restoreManager);
} else {
if (Download.checkNetworkStatus(mm)) {
restoreManager.setOnPreferenceClickListener((pref) -> {
Download.receive(
requireActivity(), new DownloadReceiver() {
@Override
public void onDownloadDone(Context context, Uri uri) {
Data.exportPrefs();
Shell.su("cp " + uri.getPath() + " /data/local/tmp/manager.apk").exec();
if (ShellUtils.fastCmdResult("pm install /data/local/tmp/manager.apk")) {
Shell.su("rm -f /data/local/tmp/manager.apk").exec();
RootUtils.uninstallPkg(context.getPackageName());
return;
}
Shell.su("rm -f /data/local/tmp/manager.apk").exec();
}
},
Data.managerLink,
Utils.fmt("MagiskManager-v%s.apk", Data.remoteManagerVersionString)
);
return true;
});
} else {
if (!Download.checkNetworkStatus(mm))
generalCatagory.removePreference(restoreManager);
}
generalCatagory.removePreference(hideManager);
}
} else {
@@ -165,16 +164,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
generalCatagory.removePreference(hideManager);
}
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
if (!showSuperuser) {
prefScreen.removePreference(suCategory);
}
if (!Shell.rootAccess()) {
prefScreen.removePreference(magiskCategory);
generalCatagory.removePreference(hideManager);
} else if (Data.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
prefScreen.removePreference(magiskCategory);
}
}
@@ -217,11 +213,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
break;
}
Data.loadConfig();
setSummary();
switch (key) {
case Const.Key.ROOT_ACCESS:
rootState = Utils.getPrefsInt(prefs, key);
break;
case Const.Key.SU_MULTIUSER_MODE:
Data.multiuserState = Utils.getPrefsInt(prefs, key);
break;
case Const.Key.SU_MNT_NS:
namespaceState = Utils.getPrefsInt(prefs, key);
break;
case Const.Key.DARK_THEME:
Topic.publish(false, Topic.RELOAD_ACTIVITY);
requireActivity().recreate();
break;
case Const.Key.COREONLY:
if (prefs.getBoolean(key, false)) {
@@ -240,20 +243,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
Shell.su("magiskhide --disable").submit();
}
break;
case Const.Key.HOSTS:
if (prefs.getBoolean(key, false)) {
Shell.su("cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE,
"mount -o bind " + Const.MAGISK_HOST_FILE + " /system/etc/hosts")
.submit();
} else {
Shell.su("umount -l /system/etc/hosts",
"rm -f " + Const.MAGISK_HOST_FILE)
.submit();
}
break;
case Const.Key.LOCALE:
LocaleManager.setLocale(mm);
Topic.publish(false, Topic.RELOAD_ACTIVITY);
requireActivity().recreate();
break;
case Const.Key.UPDATE_CHANNEL:
case Const.Key.CUSTOM_CHANNEL:
@@ -263,6 +255,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
Utils.setupUpdateCheck();
break;
}
Data.loadConfig();
setSummary();
}
@Override
@@ -272,54 +266,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
case Const.Key.SU_FINGERPRINT:
boolean checked = ((SwitchPreference) preference).isChecked();
((SwitchPreference) preference).setChecked(!checked);
CustomAlertDialog dialog = new CustomAlertDialog(requireActivity());
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
Drawable fingerprint = getResources().getDrawable(R.drawable.ic_fingerprint);
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
Resources.Theme theme = requireActivity().getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
fingerprint.setTint(ta.getColor(0, Color.GRAY));
ta.recycle();
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
vh.messageView.setGravity(Gravity.CENTER);
try {
FingerprintHelper helper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(helpString);
}
@Override
public void onAuthenticationFailed() {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(R.string.auth_fail);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
dialog.dismiss();
((SwitchPreference) preference).setChecked(checked);
mm.mDB.setSettings(key, checked ? 1 : 0);
}
};
dialog.setMessage(R.string.auth_fingerprint)
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
.setOnCancelListener(d -> helper.cancel())
.show();
helper.authenticate();
} catch (Exception e) {
e.printStackTrace();
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
}
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
((SwitchPreference) preference).setChecked(checked);
mm.mDB.setSettings(key, checked ? 1 : 0);
});
break;
}
return true;
@@ -328,8 +278,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
private void setSummary() {
updateChannel.setSummary(getResources()
.getStringArray(R.array.update_channel)[Data.updateChannel]);
suAccess.setSummary(getResources()
.getStringArray(R.array.su_access)[Data.suAccessState]);
rootConfig.setSummary(getResources()
.getStringArray(R.array.su_access)[rootState]);
autoRes.setSummary(getResources()
.getStringArray(R.array.auto_response)[Data.suResponseType]);
suNotification.setSummary(getResources()
@@ -337,10 +287,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
requestTimeout.setSummary(
getString(R.string.request_timeout_summary,
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
multiuserMode.setSummary(getResources()
.getStringArray(R.array.multiuser_summary)[Data.multiuserMode]);
namespaceMode.setSummary(getResources()
.getStringArray(R.array.namespace_summary)[Data.suNamespaceMode]);
multiuserConfig.setSummary(getResources()
.getStringArray(R.array.multiuser_summary)[Data.multiuserState]);
nsConfig.setSummary(getResources()
.getStringArray(R.array.namespace_summary)[namespaceState]);
}
@Override
@@ -352,4 +302,38 @@ public class SettingsFragment extends PreferenceFragmentCompat
public int[] getSubscribedTopics() {
return new int[] {Topic.LOCALE_FETCH_DONE};
}
@Override
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
return new PreferenceGroupAdapter(preferenceScreen) {
@SuppressLint("RestrictedApi")
@Override
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
Preference preference = getItem(position);
if (preference instanceof PreferenceCategory)
setZeroPaddingToLayoutChildren(holder.itemView);
else {
View iconFrame = holder.itemView.findViewById(R.id.icon_frame);
if (iconFrame != null) {
iconFrame.setVisibility(preference.getIcon() == null ? View.GONE : View.VISIBLE);
}
}
}
};
}
private void setZeroPaddingToLayoutChildren(View view) {
if (!(view instanceof ViewGroup))
return;
ViewGroup viewGroup = (ViewGroup) view;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
setZeroPaddingToLayoutChildren(viewGroup.getChildAt(i));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
viewGroup.setPaddingRelative(0, viewGroup.getPaddingTop(), viewGroup.getPaddingEnd(), viewGroup.getPaddingBottom());
else
viewGroup.setPadding(0, viewGroup.getPaddingTop(), viewGroup.getPaddingRight(), viewGroup.getPaddingBottom());
}
}
}

View File

@@ -1,8 +1,6 @@
package com.topjohnwu.magisk.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -15,16 +13,15 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuLogFragment extends BaseFragment {
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private Unbinder unbinder;
private SuLogAdapter adapter;
@Override
@@ -44,7 +41,7 @@ public class SuLogFragment extends BaseFragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = ButterKnife.bind(this, v);
unbinder = new SuLogFragment_ViewBinding(this, v);
adapter = new SuLogAdapter(mm.mDB);
recyclerView.setAdapter(adapter);
@@ -79,10 +76,4 @@ public class SuLogFragment extends BaseFragment {
return true;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -16,24 +14,24 @@ import com.topjohnwu.magisk.container.Policy;
import java.util.List;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuperuserFragment extends BaseFragment {
private Unbinder unbinder;
private PackageManager pm;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
private PackageManager pm;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new SuperuserFragment_ViewBinding(this, view);
pm = getActivity().getPackageManager();
pm = requireActivity().getPackageManager();
return view;
}
@@ -49,14 +47,8 @@ public class SuperuserFragment extends BaseFragment {
displayPolicyList();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void displayPolicyList() {
List<Policy> policyList = mm.mDB.getPolicyList(pm);
List<Policy> policyList = mm.mDB.getPolicyList();
if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);

View File

@@ -1,18 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.topjohnwu.magisk.services.OnBootService;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED))
OnBootService.enqueueWork(context);
}
}

View File

@@ -0,0 +1,76 @@
package com.topjohnwu.magisk.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.SuConnector;
import com.topjohnwu.superuser.Shell;
public class GeneralReceiver extends BroadcastReceiver {
private String getPkg(Intent i) {
return i.getData() == null ? "" : i.getData().getEncodedSchemeSpecificPart();
}
@Override
public void onReceive(Context context, Intent intent) {
MagiskManager mm = Data.MM();
if (intent == null)
return;
String action = intent.getAction();
if (action == null)
return;
switch (action) {
case Intent.ACTION_BOOT_COMPLETED:
String bootAction = intent.getStringExtra("action");
if (bootAction == null)
bootAction = "boot";
switch (bootAction) {
case "request":
Intent i = new Intent(mm, Data.classMap.get(SuRequestActivity.class))
.putExtra("socket", intent.getStringExtra("socket"))
.putExtra("version", 2)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mm.startActivity(i);
break;
case "log":
SuConnector.handleLogs(intent, 2);
break;
case "notify":
SuConnector.handleNotify(intent);
break;
case "boot":
default:
/* The actual on-boot trigger */
OnBootService.enqueueWork(mm);
break;
}
break;
case Intent.ACTION_PACKAGE_REPLACED:
// This will only work pre-O
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
mm.mDB.deletePolicy(getPkg(intent));
}
break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
String pkg = getPkg(intent);
mm.mDB.deletePolicy(pkg);
Shell.su("magiskhide --rm " + pkg).submit();
break;
case Const.Key.BROADCAST_MANAGER_UPDATE:
Data.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK);
DlInstallManager.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME));
break;
case Const.Key.BROADCAST_REBOOT:
Shell.su("/system/bin/reboot").submit();
break;
}
}
}

View File

@@ -1,49 +0,0 @@
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.Const;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class ManagerUpdate extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Download.receive(
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 orig = uri.getPath();
String patch = orig.substring(0, orig.lastIndexOf('.')) + "-patched.apk";
try {
JarMap apk = new JarMap(orig);
PatchAPK.patchPackageID(apk, Const.ORIG_PKG_NAME, context.getPackageName());
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(patch)));
super.onDownloadDone(context, Uri.fromFile(new File(patch)));
} catch (Exception ignored) { }
});
} else {
super.onDownloadDone(context, uri);
}
}
}
}

View File

@@ -1,32 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.superuser.Shell;
public class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
MagiskManager mm = Data.MM();
String pkg = intent.getData().getEncodedSchemeSpecificPart();
switch (intent.getAction()) {
case Intent.ACTION_PACKAGE_REPLACED:
// This will only work pre-O
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
mm.mDB.deletePolicy(pkg);
}
break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
mm.mDB.deletePolicy(pkg);
Shell.su("magiskhide --rm " + pkg).submit();
break;
}
}
}

View File

@@ -1,14 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.superuser.Shell;
public class RebootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Shell.su("/system/bin/reboot").submit();
}
}

View File

@@ -7,17 +7,19 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.support.annotation.RequiresApi;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
import androidx.annotation.RequiresApi;
public class ShortcutReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -32,11 +34,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
boolean root = Shell.rootAccess();
if (root && !(Const.USER_ID > 0 &&
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
if (Utils.showSuperUser()) {
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
.setShortLabel(mm.getString(R.string.superuser))
.setIntent(new Intent(mm, SplashActivity.class)
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "superuser")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -44,11 +45,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
.setRank(0)
.build());
}
if (root && Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
.setShortLabel(mm.getString(R.string.magiskhide))
.setIntent(new Intent(mm, SplashActivity.class)
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -59,7 +59,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
.setShortLabel(mm.getString(R.string.modules))
.setIntent(new Intent(mm, SplashActivity.class)
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "modules")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -67,8 +67,8 @@ public class ShortcutReceiver extends BroadcastReceiver {
.setRank(3)
.build());
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
.setShortLabel(mm.getString(R.string.download))
.setIntent(new Intent(mm, SplashActivity.class)
.setShortLabel(mm.getString(R.string.downloads))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "downloads")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))

View File

@@ -2,18 +2,20 @@ package com.topjohnwu.magisk.services;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.NotificationMgr;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import androidx.annotation.NonNull;
import androidx.core.app.JobIntentService;
public class OnBootService extends JobIntentService {
public static void enqueueWork(Context context) {
enqueueWork(context, OnBootService.class, Const.ID.ONBOOT_SERVICE_ID, new Intent());
enqueueWork(context, Data.classMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
}
@Override
@@ -26,6 +28,6 @@ public class OnBootService extends JobIntentService {
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
* */
if (Shell.rootAccess() && ShellUtils.fastCmdResult("mm_patch_dtbo"))
NotificationMgr.dtboPatched();
Notifications.dtboPatched();
}
}

View File

@@ -1,297 +1,21 @@
package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.FileObserver;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.io.DataInputStream;
import java.io.IOException;
import butterknife.BindView;
import butterknife.ButterKnife;
public class RequestActivity extends BaseActivity {
@BindView(R.id.su_popup) LinearLayout suPopup;
@BindView(R.id.timeout) Spinner timeout;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appNameView;
@BindView(R.id.package_name) TextView packageNameView;
@BindView(R.id.grant_btn) Button grant_btn;
@BindView(R.id.deny_btn) Button deny_btn;
@BindView(R.id.fingerprint) ImageView fingerprintImg;
@BindView(R.id.warning) TextView warning;
private String socketPath;
private LocalSocket socket;
private PackageManager pm;
private boolean hasTimeout;
private Policy policy;
private CountDownTimer timer;
private FingerprintHelper fingerprintHelper;
@Override
public int getDarkTheme() {
return R.style.SuRequest_Dark;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
pm = getPackageManager();
mm.mDB.clearOutdated();
Intent intent = getIntent();
socketPath = intent.getStringExtra("socket");
hasTimeout = intent.getBooleanExtra("timeout", true);
new FileObserver(socketPath) {
@Override
public void onEvent(int fileEvent, String path) {
if (fileEvent == FileObserver.DELETE_SELF) {
finish();
}
}
}.startWatching();
new SocketManager(this).exec();
}
@Override
public void finish() {
if (timer != null)
timer.cancel();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
super.finish();
}
private boolean cancelTimeout() {
timer.cancel();
deny_btn.setText(getString(R.string.deny));
return false;
}
private void showRequest() {
switch (Data.suResponseType) {
case Const.Value.SU_AUTO_DENY:
handleAction(Policy.DENY, 0);
return;
case Const.Value.SU_AUTO_ALLOW:
handleAction(Policy.ALLOW, 0);
return;
case Const.Value.SU_PROMPT:
default:
}
// If not interactive, response directly
if (policy.policy != Policy.INTERACTIVE) {
handleAction();
return;
}
setContentView(R.layout.activity_request);
ButterKnife.bind(this);
appIcon.setImageDrawable(policy.info.loadIcon(pm));
appNameView.setText(policy.appName);
packageNameView.setText(policy.packageName);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.allow_timeout, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
timeout.setAdapter(adapter);
timer = new CountDownTimer(Data.suRequestTimeout * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")"));
}
@Override
public void onFinish() {
deny_btn.setText(getString(R.string.deny_with_str, "(0)"));
handleAction(Policy.DENY);
}
};
boolean useFingerprint = Data.suFingerprint && FingerprintHelper.canUseFingerprint();
if (useFingerprint) {
try {
fingerprintHelper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
warning.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
warning.setText(helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
handleAction(Policy.ALLOW);
}
@Override
public void onAuthenticationFailed() {
warning.setText(R.string.auth_fail);
}
};
fingerprintHelper.authenticate();
} catch (Exception e) {
e.printStackTrace();
useFingerprint = false;
}
}
if (!useFingerprint) {
grant_btn.setOnClickListener(v -> {
handleAction(Policy.ALLOW);
timer.cancel();
});
grant_btn.requestFocus();
}
grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE);
fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE);
deny_btn.setOnClickListener(v -> {
handleAction(Policy.DENY);
timer.cancel();
});
suPopup.setOnClickListener(v -> cancelTimeout());
timeout.setOnTouchListener((v, event) -> cancelTimeout());
if (hasTimeout) {
timer.start();
} else {
cancelTimeout();
}
}
@Override
public void onBackPressed() {
if (policy != null) {
handleAction(Policy.DENY);
} else {
finish();
}
}
void handleAction() {
String response;
if (policy.policy == Policy.ALLOW) {
response = "socket:ALLOW";
} else {
response = "socket:DENY";
}
try {
socket.getOutputStream().write((response).getBytes());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent(this, Data.classMap.get(SuRequestActivity.class))
.putExtra("socket", getIntent().getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
void handleAction(int action) {
handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]);
}
void handleAction(int action, int time) {
policy.policy = action;
if (time >= 0) {
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
mm.mDB.addPolicy(policy);
}
handleAction();
}
private class SocketManager extends ParallelTask<Void, Void, Boolean> {
SocketManager(BaseActivity context) {
super(context);
}
@Override
protected Boolean doInBackground(Void... params) {
try {
socket = new LocalSocket();
socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM));
DataInputStream is = new DataInputStream(socket.getInputStream());
ContentValues payload = new ContentValues();
while (true) {
int nameLen = is.readInt();
byte[] nameBytes = new byte[nameLen];
is.readFully(nameBytes);
String name = new String(nameBytes);
if (TextUtils.equals(name, "eof"))
break;
int dataLen = is.readInt();
byte[] dataBytes = new byte[dataLen];
is.readFully(dataBytes);
String data = new String(dataBytes);
payload.put(name, data);
}
if (payload.getAsInteger("uid") == null) {
return false;
}
int uid = payload.getAsInteger("uid");
policy = mm.mDB.getPolicy(uid);
if (policy == null) {
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) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
showRequest();
} else {
finish();
}
}
}
}

View File

@@ -3,88 +3,14 @@ package com.topjohnwu.magisk.superuser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Process;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.utils.Utils;
import java.util.Date;
import com.topjohnwu.magisk.utils.SuConnector;
public class SuReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int fromUid, toUid, pid, mode;
String command, action;
Policy policy;
MagiskManager mm = Data.MM();
if (intent == null) return;
mode = intent.getIntExtra("mode", -1);
if (mode < 0) return;
if (mode == Const.Value.NOTIFY_USER_TO_OWNER) {
Utils.toast(R.string.multiuser_hint_owner_request, Toast.LENGTH_LONG);
return;
}
fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return; // Don't show anything if it's Magisk Manager
action = intent.getStringExtra("action");
if (action == null) return;
policy = mm.mDB.getPolicy(fromUid);
if (policy == null) {
try {
policy = new Policy(fromUid, mm.getPackageManager());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return;
}
}
SuLogEntry log = new SuLogEntry(policy);
String message;
switch (action) {
case "allow":
message = mm.getString(R.string.su_allow_toast, policy.appName);
log.action = true;
break;
case "deny":
message = mm.getString(R.string.su_deny_toast, policy.appName);
log.action = false;
break;
default:
return;
}
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
Utils.toast(message, Toast.LENGTH_SHORT);
if (mode == Const.Value.NOTIFY_NORMAL_LOG && policy.logging) {
toUid = intent.getIntExtra("to.uid", -1);
if (toUid < 0) return;
pid = intent.getIntExtra("pid", -1);
if (pid < 0) return;
command = intent.getStringExtra("command");
if (command == null) return;
log.toUid = toUid;
log.fromPid = pid;
log.command = command;
log.date = new Date();
mm.mDB.addLog(log);
}
if (intent != null)
SuConnector.handleLogs(intent, 1);
}
}

View File

@@ -1,12 +1,12 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.Keep;
import com.topjohnwu.utils.SignBoot;
import java.io.FileInputStream;
import java.io.InputStream;
import androidx.annotation.Keep;
public class BootSigner {
@Keep

View File

@@ -0,0 +1,113 @@
package com.topjohnwu.magisk.utils;
import android.os.AsyncTask;
import com.androidnetworking.AndroidNetworking;
import com.androidnetworking.error.ANError;
import com.androidnetworking.interfaces.DownloadListener;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class DlInstallManager {
public static void upgrade(String name) {
dlInstall(name, new PatchPackageName());
}
public static void restore() {
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
dlInstall(name, new RestoreManager());
}
public static void dlInstall(String name, ManagerDownloadListener listener) {
MagiskManager mm = Data.MM();
File apk = new File(mm.getFilesDir(), "manager.apk");
ProgressNotification progress = new ProgressNotification(name);
listener.setInstances(apk, progress);
AndroidNetworking
.download(Data.managerLink, apk.getParent(), apk.getName())
.setExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.build()
.setDownloadProgressListener(progress)
.startDownload(listener);
}
public abstract static class ManagerDownloadListener implements DownloadListener {
private File apk;
private ProgressNotification progress;
private void setInstances(File apk, ProgressNotification progress) {
this.apk = apk;
this.progress = progress;
}
public abstract void onDownloadComplete(File apk, ProgressNotification progress);
@Override
public final void onDownloadComplete() {
onDownloadComplete(apk, progress);
}
@Override
public void onError(ANError anError) {
progress.dlFail();
}
}
private static class PatchPackageName extends ManagerDownloadListener {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
File patched = apk;
MagiskManager mm = Data.MM();
if (!mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
progress.getNotification()
.setProgress(0, 0, true)
.setContentTitle(mm.getString(R.string.hide_manager_title))
.setContentText("");
progress.update();
patched = new File(apk.getParent(), "patched.apk");
try {
JarMap jarMap = new JarMap(apk);
PatchAPK.patch(jarMap, mm.getPackageName());
SignAPK.sign(jarMap, new BufferedOutputStream(new FileOutputStream(patched)));
} catch (Exception e) {
return;
}
}
APKInstall.install(mm, patched);
progress.dismiss();
}
}
private static class RestoreManager extends ManagerDownloadListener {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
MagiskManager mm = Data.MM();
progress.getNotification()
.setProgress(0, 0, true)
.setContentTitle(mm.getString(R.string.restore_img_msg))
.setContentText("");
progress.update();
Data.exportPrefs();
// Make it world readable
apk.setReadable(true, false);
if (ShellUtils.fastCmdResult("pm install " + apk))
RootUtils.rmAndLaunch(mm.getPackageName(), BuildConfig.APPLICATION_ID);
progress.dismiss();
}
}
}

View File

@@ -1,17 +1,26 @@
package com.topjohnwu.magisk.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.view.Gravity;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import java.security.KeyStore;
@@ -26,6 +35,16 @@ public abstract class FingerprintHelper {
private Cipher cipher;
private CancellationSignal cancel;
public static boolean useFingerPrint() {
MagiskManager mm = Data.MM();
boolean fp = mm.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
if (fp && !canUseFingerprint()) {
mm.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
fp = false;
}
return fp;
}
public static boolean canUseFingerprint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return false;
@@ -35,6 +54,56 @@ public abstract class FingerprintHelper {
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
}
public static void showAuthDialog(Activity activity, Runnable onSuccess) {
CustomAlertDialog dialog = new CustomAlertDialog(activity);
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
try {
FingerprintHelper helper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(helpString);
}
@Override
public void onAuthenticationFailed() {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(R.string.auth_fail);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
dialog.dismiss();
onSuccess.run();
}
};
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
Resources.Theme theme = activity.getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
fingerprint.setTint(ta.getColor(0, Color.GRAY));
ta.recycle();
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
vh.messageView.setGravity(Gravity.CENTER);
dialog.setMessage(R.string.auth_fingerprint)
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
.setOnCancelListener(d -> helper.cancel())
.show();
helper.authenticate();
} catch (Exception e) {
e.printStackTrace();
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
}
}
protected FingerprintHelper() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = Data.MM().getSystemService(FingerprintManager.class);

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.Keep;
public interface ISafetyNetHelper {
int RESPONSE_ERR = 0x01;
@@ -10,14 +8,11 @@ public interface ISafetyNetHelper {
int BASIC_PASS = 0x10;
int CTS_PASS = 0x20;
@Keep
void attest();
@Keep
int getVersion();
interface Callback {
@Keep
void onResponse(int responseCode);
}
}

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.utils;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.support.annotation.StringRes;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
@@ -16,6 +15,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import androidx.annotation.StringRes;
public class LocaleManager {
public static Locale locale = Locale.getDefault();
public final static Locale defaultLocale = Locale.getDefault();

View File

@@ -1,86 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.receivers.ManagerUpdate;
import com.topjohnwu.magisk.receivers.RebootReceiver;
public class NotificationMgr {
public static void magiskUpdate() {
MagiskManager mm = Data.MM();
Intent intent = new Intent(mm, SplashActivity.class);
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
stackBuilder.addParentStack(SplashActivity.class);
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.magisk_update_title))
.setContentText(mm.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void managerUpdate() {
MagiskManager mm = Data.MM();
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
Intent intent = new Intent(mm, ManagerUpdate.class);
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.manager_update_title))
.setContentText(mm.getString(R.string.manager_download_install))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void dtboPatched() {
MagiskManager mm = Data.MM();
Intent intent = new Intent(mm, RebootReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
.setVibrate(new long[]{0, 100, 100, 100})
.addAction(R.drawable.ic_refresh, mm.getString(R.string.reboot), pendingIntent);
NotificationManager notificationManager =
(NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
}
}

View File

@@ -1,7 +1,6 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
@@ -14,6 +13,8 @@ import com.topjohnwu.superuser.io.SuFile;
import java.io.File;
import java.io.InputStream;
import androidx.annotation.NonNull;
public class RootUtils extends Shell.Initializer {
static {
@@ -24,32 +25,27 @@ public class RootUtils extends Shell.Initializer {
Shell.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg).exec();
}
public static void rmAndLaunch(String rm, String launch) {
Shell.su(Utils.fmt("(rm_launch %d %s %s)&", Const.USER_ID, rm, launch)).exec();
}
@Override
public boolean onInit(Context context, @NonNull Shell shell) {
Shell.Job job = shell.newJob();
if (shell.isRoot()) {
InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions);
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils);
job.add(magiskUtils).add(managerUtils);
if (!new SuFile("/sbin/.magisk").exists())
job.add("ln -s /sbin/.core /sbin/.magisk");
job.add(context.getResources().openRawResource(R.raw.util_functions))
.add(context.getResources().openRawResource(R.raw.utils));
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");
Data.loadMagiskInfo();
} else {
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
job.add(nonroot);
}
job.add("mount_partitions", "get_flags", "run_migrations").exec();
job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
Data.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));

View File

@@ -0,0 +1,157 @@
package com.topjohnwu.magisk.utils;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.LocalSocket;
import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
public abstract class SuConnector {
protected LocalSocket socket = new LocalSocket();
protected DataOutputStream out;
protected DataInputStream in;
public SuConnector(String name) throws IOException {
connect(name);
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
}
private String readString() throws IOException {
int len = in.readInt();
byte[] buf = new byte[len];
in.readFully(buf);
return new String(buf, "UTF-8");
}
public Bundle readSocketInput() throws IOException {
Bundle bundle = new Bundle();
while (true) {
String name = readString();
if (TextUtils.equals(name, "eof"))
break;
bundle.putString(name, readString());
}
return bundle;
}
public void response() {
try {
onResponse();
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
in.close();
out.close();
socket.close();
} catch (IOException ignored) { }
}
public abstract void connect(String name) throws IOException;
protected abstract void onResponse() throws IOException;
public static void handleLogs(Intent intent, int version) {
int fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
MagiskManager mm = Data.MM();
PackageManager pm = mm.getPackageManager();
Policy policy;
boolean notify;
Bundle data = intent.getExtras();
if (data.containsKey("notify")) {
notify = data.getBoolean("notify");
try {
policy = new Policy(fromUid, pm);
} catch (PackageManager.NameNotFoundException e) {
return;
}
} else {
// Doesn't report whether notify or not, check database ourselves
policy = mm.mDB.getPolicy(fromUid);
if (policy == null)
return;
notify = policy.notification;
}
if (version == 1) {
String action = intent.getStringExtra("action");
if (action == null) return;
switch (action) {
case "allow":
policy.policy = Policy.ALLOW;
break;
case "deny":
policy.policy = Policy.DENY;
break;
default:
return;
}
} else {
policy.policy = data.getInt("policy", -1);
if (policy.policy < 0)
return;
}
if (notify)
handleNotify(policy);
SuLogEntry log = new SuLogEntry(policy);
int toUid = intent.getIntExtra("to.uid", -1);
if (toUid < 0) return;
int pid = intent.getIntExtra("pid", -1);
if (pid < 0) return;
String command = intent.getStringExtra("command");
if (command == null) return;
log.toUid = toUid;
log.fromPid = pid;
log.command = command;
log.date = new Date();
mm.mDB.addLog(log);
}
private static void handleNotify(Policy policy) {
MagiskManager mm = Data.MM();
String message = mm.getString(policy.policy == Policy.ALLOW ?
R.string.su_allow_toast : R.string.su_deny_toast, policy.appName);
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
Utils.toast(message, Toast.LENGTH_SHORT);
}
public static void handleNotify(Intent intent) {
MagiskManager mm = Data.MM();
int fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
try {
Policy policy = new Policy(fromUid, mm.getPackageManager());
policy.policy = intent.getIntExtra("policy", -1);
if (policy.policy >= 0)
handleNotify(policy);
} catch (PackageManager.NameNotFoundException ignored) {}
}
}

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.IntDef;
import com.topjohnwu.magisk.Data;
import java.lang.annotation.Retention;
@@ -9,23 +7,24 @@ import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
import androidx.annotation.IntDef;
public class Topic {
public static final int MAGISK_HIDE_DONE = 0;
public static final int RELOAD_ACTIVITY = 1;
public static final int MODULE_LOAD_DONE = 2;
public static final int REPO_LOAD_DONE = 3;
public static final int UPDATE_CHECK_DONE = 4;
public static final int SNET_CHECK_DONE = 5;
public static final int LOCALE_FETCH_DONE = 6;
public static final int MODULE_LOAD_DONE = 1;
public static final int REPO_LOAD_DONE = 2;
public static final int UPDATE_CHECK_DONE = 3;
public static final int SNET_CHECK_DONE = 4;
public static final int LOCALE_FETCH_DONE = 5;
@IntDef({MAGISK_HIDE_DONE, RELOAD_ACTIVITY, MODULE_LOAD_DONE, REPO_LOAD_DONE,
@IntDef({MAGISK_HIDE_DONE, MODULE_LOAD_DONE, REPO_LOAD_DONE,
UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCALE_FETCH_DONE})
@Retention(RetentionPolicy.SOURCE)
public @interface TopicID {}
// We will not dynamically add topics, so use arrays instead of hash tables
private static Store[] topicList = new Store[7];
private static Store[] topicList = new Store[6];
public static void subscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) {

View File

@@ -6,12 +6,17 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.OpenableColumns;
import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
@@ -19,6 +24,7 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import java.util.Locale;
@@ -77,7 +83,7 @@ public class Utils {
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(mm, UpdateCheckService.class);
ComponentName service = new ComponentName(mm, Data.classMap.get(UpdateCheckService.class));
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
@@ -113,13 +119,40 @@ public class Utils {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
Map<String, Module> moduleMap = new ValueSortedMap<>();
SuFile path = new SuFile(Const.MAGISK_PATH);
String[] modules = path.list(
SuFile[] modules = path.listFiles(
(file, name) -> !name.equals("lost+found") && !name.equals(".core"));
for (String name : modules) {
Module module = new Module(Const.MAGISK_PATH + "/" + name);
for (SuFile file : modules) {
if (file.isFile()) continue;
Module module = new Module(Const.MAGISK_PATH + "/" + file.getName());
moduleMap.put(module.getId(), module);
}
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap);
});
}
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
try {
if (info.labelRes > 0) {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
}
} catch (Exception ignored) {}
return info.loadLabel(pm).toString();
}
public static boolean showSuperUser() {
if (Data.multiuserState < 0)
Data.multiuserState = Data.MM().mDB.getSettings(Const.Key.SU_MULTIUSER_MODE,
Const.Value.MULTIUSER_MODE_OWNER_ONLY);
return Shell.rootAccess() && (Const.USER_ID == 0 ||
Data.multiuserState != Const.Value.MULTIUSER_MODE_OWNER_MANAGED);
}
public static String dlString(String url) {
String s = (String) AndroidNetworking.get(url).build().executeForString().getResult();
return s == null ? "" : s;
}
}

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -54,42 +54,42 @@
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
</LinearLayout>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/app_version_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_info_outline"
app:text="@string/app_version"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/app_changelog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_history"
app:text="@string/app_changelog"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/app_translators"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_language"
app:text="@string/app_translators"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/follow_twitter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_twitter"
app:text="@string/follow_twitter"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/app_source_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_github"
app:text="@string/app_source_code"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/support_thread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -98,7 +98,7 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -40,14 +40,14 @@
android:text="@string/donation"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/paypal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_paypal"
app:text="PayPal"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.l
android:id="@+id/patreon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -56,7 +56,7 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -42,7 +42,7 @@
android:visibility="gone">
<Button
android:id="@+id/no_thanks"
android:id="@+id/close"
style="?android:borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="50dp"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
@@ -24,7 +24,7 @@
</FrameLayout>
<android.support.design.widget.NavigationView
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@@ -32,4 +32,4 @@
android:fitsSystemWindows="true"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>
</androidx.drawerlayout.widget.DrawerLayout>

View File

@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/su_popup"
tools:context=".superuser.RequestActivity"
tools:context=".SuRequestActivity"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="350dp"

View File

@@ -7,7 +7,7 @@
android:orientation="vertical"
tools:context="com.topjohnwu.magisk.fragments.LogFragment">
<android.support.design.widget.TabLayout
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
@@ -17,10 +17,10 @@
android:elevation="4dp"
android:visibility="gone">
</android.support.design.widget.TabLayout>
</com.google.android.material.tabs.TabLayout>
<android.support.v4.view.ViewPager
<androidx.viewpager.widget.ViewPager
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="@+id/container"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipeRefreshLayout"
@@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/core_only_notice"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -55,9 +55,9 @@
android:text="@string/settings_core_only_title"
android:textStyle="bold" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
style="?attr/cardStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -141,9 +141,9 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/safetyNet_card"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -259,9 +259,9 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/install_option_card"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -308,9 +308,9 @@
android:text="@string/keep_dm_verity" />
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/install_button"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -361,9 +361,9 @@
</RelativeLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/uninstall_button"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -392,10 +392,10 @@
android:textSize="20sp"
android:textStyle="bold" />
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

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