Compare commits

..

251 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
350 changed files with 15954 additions and 12203 deletions

4
.gitignore vendored
View File

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

View File

@@ -1,7 +1,15 @@
# Magisk # 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 ## Building Environment Requirements
1. Python 3.5+: run `build.py` script 1. Python 3.5+: run `build.py` script
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips 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 3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
@@ -9,13 +17,11 @@
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes 5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
## Building Notes and Instructions ## Building Notes and Instructions
1. Building is supported on macOS, Linux, and Windows using the custom NDK: [FrankeNDK](https://github.com/topjohnwu/FrankeNDK). 1. Clone sources with submodules: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example. 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. 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` 3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
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). 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).
## Documentation
[Link to Documentation](docs/README.MD)
## License ## License

View File

@@ -1,7 +1,8 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
def configProps = new Properties() 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 { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
@@ -69,15 +70,23 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.core:core:1.0.1'
fullImplementation project(':utils') fullImplementation project(':utils')
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}" fullImplementation 'com.amitshekhar.android:android-networking:1.0.2'
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}" fullImplementation 'androidx.appcompat:appcompat:1.0.2'
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}" fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}" fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}" fullImplementation "androidx.cardview:cardview:${rootProject.ext.androidXVersion}"
fullImplementation 'com.github.topjohnwu:libsu:2.0.1' 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 'com.atlassian.commonmark:commonmark:0.11.0'
fullImplementation 'org.kamranzafar:jtar:2.3' 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 *; # public *;
#} #}
# Don't obfuscate, we are open source anyway :)
-dontobfuscate
# BouncyCastle # BouncyCastle
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; } -keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; } -keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; } -keep,allowoptimization class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
-dontwarn javax.naming.** -dontwarn javax.naming.**
# Gson # Snet extention
-keepattributes Signature -keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
# Fast Android Networking Library
-dontwarn okhttp3.**
# Strip logging # Strip logging
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger { -assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
public *** debug(...); public *** debug(...);
} }
# Excessive obfuscation
-repackageclasses 'a'
-allowaccessmodification
-optimizationpasses 6

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.magisk"> package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
@@ -8,15 +9,18 @@
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:name=".MagiskManager" android:name="a.q"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<!-- Activities -->
<activity <activity
android:name=".MainActivity" android:name="a.b"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true" /> android:exported="true" />
<activity <activity
android:name=".SplashActivity" android:name="a.c"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true" android:exported="true"
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
@@ -26,53 +30,64 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".AboutActivity" android:name="a.d"
android:theme="@style/AppTheme.StatusBar" /> android:theme="@style/AppTheme.StatusBar" />
<activity <activity
android:name=".DonationActivity" android:name="a.e"
android:theme="@style/AppTheme.StatusBar"/> android:theme="@style/AppTheme.StatusBar"/>
<activity <activity
android:name=".FlashActivity" android:name="a.f"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor" android:screenOrientation="nosensor"
android:theme="@style/AppTheme.StatusBar" /> android:theme="@style/AppTheme.StatusBar" />
<activity <activity
android:name=".NoUIActivity" android:name="a.g"
android:theme="@style/AppTheme.Translucent" /> android:theme="@style/AppTheme.Translucent" />
<!-- Superuser -->
<activity <activity
android:name=".superuser.RequestActivity" android:name="a.m"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:launchMode="singleTask" android:launchMode="singleTask"
android:taskAffinity="internal.superuser" android:taskAffinity="internal.superuser"
android:theme="@style/SuRequest" /> 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=".superuser.SuReceiver" />
<receiver android:name=".receivers.BootReceiver">
<!-- Receiver -->
<receiver android:name="a.h">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver>
<receiver android:name=".receivers.PackageReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" /> <action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" /> <data android:scheme="package" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.ManagerUpdate" /> <receiver android:name="a.i">
<receiver android:name=".receivers.RebootReceiver" />
<receiver android:name=".receivers.ShortcutReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- Service -->
<service <service
android:name=".services.OnBootService" android:name="a.j"
android:exported="true" android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" /> android:permission="android.permission.BIND_JOB_SERVICE" />
<service <service
android:name=".services.UpdateCheckService" android:name="a.k"
android:exported="true" android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" /> 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.net.Uri;
import android.os.Bundle; 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.text.TextUtils;
import android.view.View; import android.view.View;
@@ -15,8 +12,10 @@ import com.topjohnwu.magisk.utils.Utils;
import java.util.Locale; import java.util.Locale;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
public class AboutActivity extends BaseActivity { public class AboutActivity extends BaseActivity {
@@ -37,7 +36,7 @@ public class AboutActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about); setContentView(R.layout.activity_about);
ButterKnife.bind(this); new AboutActivity_ViewBinding(this);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish()); toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -1,5 +1,6 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.os.Environment;
import android.os.Process; import android.os.Process;
import java.io.File; import java.io.File;
@@ -9,7 +10,6 @@ import java.util.List;
public class Const { public class Const {
public static final String DEBUG_TAG = "MagiskManager"; 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"; public static final String MAGISKHIDE_PROP = "persist.magisk.hide";
// APK content // APK content
@@ -18,30 +18,28 @@ public class Const {
public static final String SU_KEYSTORE_KEY = "su_key"; public static final String SU_KEYSTORE_KEY = "su_key";
// Paths // 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_DISABLE_FILE;
public static File MAGISK_HOST_FILE;
static { static {
/* Prevent crashing on unrooted devices */ MAGISK_DISABLE_FILE = new File("xxx");
MAGISK_PATH = MAGISK_DISABLE_FILE = MAGISK_HOST_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 TMP_FOLDER_PATH = "/dev/tmp";
public static final String MAGISK_LOG = "/cache/magisk.log"; public static final String MAGISK_LOG = "/cache/magisk.log";
public static final String MANAGER_CONFIGS = ".tmp.magisk.config"; public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
// Versions // Versions
public static final int UPDATE_SERVICE_VER = 1; public static final int UPDATE_SERVICE_VER = 1;
public static final int MIN_MODULE_VER = 1500;
public static int MIN_MODULE_VER() { public static final int SNET_EXT_VER = 12;
return Data.magiskVersionCode >= MAGISK_VER.REMOVE_LEGACY_LINK ? 1500 : 1400;
}
/* A list of apps that should not be shown as hide-able */ /* A list of apps that should not be shown as hide-able */
public static final List<String> HIDE_BLACKLIST = Arrays.asList( public static final List<String> HIDE_BLACKLIST = Arrays.asList(
"android",
Data.MM().getPackageName(), Data.MM().getPackageName(),
"com.google.android.gms" "com.google.android.gms"
); );
@@ -49,15 +47,11 @@ public class Const {
public static final int USER_ID = Process.myUid() / 100000; public static final int USER_ID = Process.myUid() / 100000;
public static final class MAGISK_VER { 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 SEPOL_REFACTOR = 1640;
public static final int FIX_ENV = 1650; public static final int FIX_ENV = 1650;
public static final int DBVER_SIX = 17000; 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 { public static class ID {
@@ -70,20 +64,23 @@ public class Const {
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4; public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
public static final int APK_UPDATE_NOTIFICATION_ID = 5; public static final int APK_UPDATE_NOTIFICATION_ID = 5;
public static final int DTBO_NOTIFICATION_ID = 7; 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 class Url {
public static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/stable.json"; 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 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 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 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 PATREON_URL = "https://www.patreon.com/topjohnwu";
public static final String TWITTER_URL = "https://twitter.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 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 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 // intents
public static final String OPEN_SECTION = "section"; 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 INTENT_SET_LINK = "link";
public static final String FLASH_ACTION = "action"; public static final String FLASH_ACTION = "action";
public static final String FLASH_SET_BOOT = "boot"; 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 // others
public static final String CHECK_UPDATES = "check_update"; 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 NAMESPACE_MODE_ISOLATE = 2;
public static final int NO_NOTIFICATION = 0; public static final int NO_NOTIFICATION = 0;
public static final int NOTIFICATION_TOAST = 1; 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_PROMPT = 0;
public static final int SU_AUTO_DENY = 1; public static final int SU_AUTO_DENY = 1;
public static final int SU_AUTO_ALLOW = 2; public static final int SU_AUTO_ALLOW = 2;

View File

@@ -5,7 +5,11 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Xml; 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.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils; import com.topjohnwu.superuser.ShellUtils;
@@ -18,11 +22,14 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class Data { public class Data {
// Global app instance // Global app instance
public static WeakReference<MagiskManager> weakApp; public static WeakReference<MagiskManager> weakApp;
public static Handler mainHandler = new Handler(Looper.getMainLooper()); public static Handler mainHandler = new Handler(Looper.getMainLooper());
public static Map<Class, Class> classMap = new HashMap<>();
// Current status // Current status
public static String magiskVersionString; public static String magiskVersionString;
@@ -40,8 +47,6 @@ public class Data {
public static String managerLink; public static String managerLink;
public static String managerNoteLink; public static String managerNoteLink;
public static String uninstallerLink; public static String uninstallerLink;
public static int snetVersionCode;
public static String snetLink;
// Install flags // Install flags
public static boolean keepVerity = false; public static boolean keepVerity = false;
@@ -51,22 +56,39 @@ public class Data {
public static boolean isDarkTheme; public static boolean isDarkTheme;
public static int suRequestTimeout; public static int suRequestTimeout;
public static int suLogTimeout = 14; public static int suLogTimeout = 14;
public static int suAccessState; public static int multiuserState = -1;
public static boolean suFingerprint;
public static int multiuserMode;
public static int suResponseType; public static int suResponseType;
public static int suNotificationType; public static int suNotificationType;
public static int suNamespaceMode;
public static int updateChannel; public static int updateChannel;
public static int repoOrder; 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() { public static void loadMagiskInfo() {
try { try {
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0]; magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V")); magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ? if (magiskVersionCode >= Const.MAGISK_VER.HIDE_STATUS) {
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP); magiskHide = Shell.su("magiskhide --status").exec().isSuccess();
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0; } else {
String s = ShellUtils.fastCmd(("resetprop -p ") + Const.MAGISKHIDE_PROP);
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
}
} catch (NumberFormatException ignored) {} } catch (NumberFormatException ignored) {}
} }
@@ -80,12 +102,12 @@ public class Data {
mm.prefs.edit().commit(); mm.prefs.edit().commit();
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs", File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
mm.getPackageName() + "_preferences.xml"); 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() { public static void importPrefs() {
MagiskManager mm = MM(); 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()) { if (config.exists()) {
SharedPreferences.Editor editor = mm.prefs.edit(); SharedPreferences.Editor editor = mm.prefs.edit();
try { try {
@@ -150,15 +172,6 @@ public class Data {
suRequestTimeout = Utils.getPrefsInt(mm.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]); 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); 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); 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 // config
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false); isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
@@ -170,15 +183,10 @@ public class Data {
MM().prefs.edit() MM().prefs.edit()
.putBoolean(Const.Key.DARK_THEME, isDarkTheme) .putBoolean(Const.Key.DARK_THEME, isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, magiskHide) .putBoolean(Const.Key.MAGISKHIDE, magiskHide)
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_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_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType)) .putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType)) .putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel)) .putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER) .putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
.putInt(Const.Key.REPO_ORDER, repoOrder) .putInt(Const.Key.REPO_ORDER, repoOrder)

View File

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

View File

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

View File

@@ -1,12 +1,10 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.preference.PreferenceManager; 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.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.LocaleManager; import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils; import com.topjohnwu.magisk.utils.RootUtils;
@@ -22,7 +20,7 @@ public class MagiskManager extends ContainerApp {
// Global resources // Global resources
public SharedPreferences prefs; public SharedPreferences prefs;
public MagiskDatabaseHelper mDB; public MagiskDB mDB;
public RepoDatabaseHelper repoDB; public RepoDatabaseHelper repoDB;
public MagiskManager() { public MagiskManager() {
@@ -36,22 +34,11 @@ public class MagiskManager extends ContainerApp {
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER); Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER);
Shell.Config.verboseLogging(BuildConfig.DEBUG); Shell.Config.verboseLogging(BuildConfig.DEBUG);
Shell.Config.setInitializer(RootUtils.class); Shell.Config.setInitializer(RootUtils.class);
Shell.Config.setTimeout(2);
prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs = PreferenceManager.getDefaultSharedPreferences(this);
mDB = MagiskDatabaseHelper.getInstance(this); mDB = MagiskDB.getInstance();
repoDB = new RepoDatabaseHelper(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) {}
}
LocaleManager.setLocale(this); LocaleManager.setLocale(this);
Data.loadConfig(); Data.loadConfig();

View File

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

View File

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

View File

@@ -1,18 +1,18 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import com.topjohnwu.magisk.asyncs.CheckUpdates; import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.UpdateRepos; import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.BaseActivity; 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.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.utils.Download; import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.LocaleManager; import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
@@ -22,6 +22,19 @@ public class SplashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(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 // Magisk working as expected
if (Shell.rootAccess() && Data.magiskVersionCode > 0) { if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
// Update check service // Update check service
@@ -30,21 +43,16 @@ public class SplashActivity extends BaseActivity {
Utils.loadModules(); Utils.loadModules();
} }
mm.repoDB = new RepoDatabaseHelper(this);
Data.importPrefs(); Data.importPrefs();
// Dynamic detect all locales // Dynamic detect all locales
LocaleManager.loadAvailableLocales(); LocaleManager.loadAvailableLocales();
// Create notification channel on Android O // Create notification channel on Android O
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Notifications.setup(this);
NotificationChannel channel = new NotificationChannel(Const.ID.NOTIFICATION_CHANNEL,
getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT);
getSystemService(NotificationManager.class).createNotificationChannel(channel);
}
// Setup shortcuts // Setup shortcuts
sendBroadcast(new Intent(this, ShortcutReceiver.class)); sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
if (Download.checkNetworkStatus(this)) { if (Download.checkNetworkStatus(this)) {
// Fire update check // Fire update check
@@ -58,7 +66,7 @@ public class SplashActivity extends BaseActivity {
mm.hasInit = true; 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(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM)); intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
startActivity(intent); 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.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -19,8 +15,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import java.util.ArrayList; import java.util.ArrayList;
@@ -28,8 +24,9 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> { public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
@@ -53,25 +50,12 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return new ViewHolder(v); 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() { private void loadApps() {
fullList = pm.getInstalledApplications(0); fullList = pm.getInstalledApplications(0);
hideList = Shell.su("magiskhide --ls").exec().getOut(); hideList = Shell.su("magiskhide --ls").exec().getOut();
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) { for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next(); ApplicationInfo info = i.next();
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) { if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) {
i.remove(); i.remove();
} }
} }
@@ -79,7 +63,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
boolean ah = hideList.contains(a.packageName); boolean ah = hideList.contains(a.packageName);
boolean bh = hideList.contains(b.packageName); boolean bh = hideList.contains(b.packageName);
if (ah == bh) { 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) { } else if (ah) {
return -1; return -1;
} else { } else {
@@ -94,7 +79,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ApplicationInfo info = showList.get(position); ApplicationInfo info = showList.get(position);
holder.appIcon.setImageDrawable(info.loadIcon(pm)); holder.appIcon.setImageDrawable(info.loadIcon(pm));
holder.appName.setText(getLabel(info)); holder.appName.setText(Utils.getAppLabel(info, pm));
holder.appPackage.setText(info.packageName); holder.appPackage.setText(info.packageName);
holder.checkBox.setOnCheckedChangeListener(null); holder.checkBox.setOnCheckedChangeListener(null);
@@ -132,11 +117,11 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(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) { private boolean lowercaseContains(String s, CharSequence filter) {
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter); return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
@@ -150,7 +135,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
showList = new ArrayList<>(); showList = new ArrayList<>();
String filter = constraint.toString().toLowerCase(); String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : fullList) { for (ApplicationInfo info : fullList) {
if (lowercaseContains(getLabel(info), filter) if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|| lowercaseContains(info.packageName, filter)) { || lowercaseContains(info.packageName, filter)) {
showList.add(info); showList.add(info);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
package com.topjohnwu.magisk.asyncs; 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.BuildConfig;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; 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.Topic;
import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@@ -42,64 +42,66 @@ public class CheckUpdates {
} }
} }
public static void fetchUpdates() { public static void check(Runnable cb) {
String jsonStr = ""; String url;
switch (Data.updateChannel) { switch (Data.updateChannel) {
case Const.Value.STABLE_CHANNEL: case Const.Value.STABLE_CHANNEL:
jsonStr = WebService.getString(Const.Url.STABLE_URL); url = Const.Url.STABLE_URL;
break; break;
case Const.Value.BETA_CHANNEL: case Const.Value.BETA_CHANNEL:
jsonStr = WebService.getString(Const.Url.BETA_URL); url = Const.Url.BETA_URL;
break; break;
case Const.Value.CUSTOM_CHANNEL: 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; break;
default:
return;
} }
AndroidNetworking.get(url).build().getAsJSONObject(new UpdateListener(cb));
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);
});
} }
public static void check() { public static void check() {
check(null); 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.app.ProgressDialog;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
@@ -15,7 +14,6 @@ import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.TarEntry; import com.topjohnwu.magisk.container.TarEntry;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.magisk.utils.ZipUtils; import com.topjohnwu.magisk.utils.ZipUtils;
@@ -44,6 +42,8 @@ import java.net.HttpURLConnection;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull;
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> { public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private static final int PATCH_MODE = 0; 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)) { if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
console.add("- Downloading zip"); 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 = new BufferedInputStream(new ProgressStream(conn), conn.getContentLength());
buf.mark(conn.getContentLength() + 1); buf.mark(conn.getContentLength() + 1);
try (OutputStream out = new FileOutputStream(zip)) { try (OutputStream out = new FileOutputStream(zip)) {
@@ -242,7 +242,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
switch (mode) { switch (mode) {
case PATCH_MODE: case PATCH_MODE:
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img"); 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(); dest.getParentFile().mkdirs();
OutputStream out; OutputStream out;
switch (fmt) { switch (fmt) {

View File

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

View File

@@ -1,41 +1,53 @@
package com.topjohnwu.magisk.asyncs; package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.RootUtils; import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.ShellUtils; import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileOutputStream;
import com.topjohnwu.utils.JarMap; import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK; import com.topjohnwu.utils.SignAPK;
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.security.SecureRandom;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
public class PatchAPK { 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) { private static String genPackageName(String prefix, int length) {
StringBuilder builder = new StringBuilder(length); StringBuilder builder = new StringBuilder(length);
builder.append(prefix); builder.append(prefix);
length -= prefix.length(); length -= prefix.length();
SecureRandom random = new SecureRandom(); SecureRandom random = new SecureRandom();
String base = "abcdefghijklmnopqrstuvwxyz"; char next, prev = '.';
String alpha = base + base.toUpperCase();
String full = alpha + "0123456789..........";
char next, prev = '\0';
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
if (prev == '.' || i == length - 1 || i == 0) { if (prev == '.' || i == length - 1) {
next = alpha.charAt(random.nextInt(alpha.length())); next = ALPHA.charAt(random.nextInt(ALPHA.length()));
} else { } else {
next = full.charAt(random.nextInt(full.length())); next = ALPHANUMDOTS.charAt(random.nextInt(ALPHANUMDOTS.length()));
} }
builder.append(next); builder.append(next);
prev = next; prev = next;
@@ -43,88 +55,81 @@ public class PatchAPK {
return builder.toString(); 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; 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; boolean match = true;
for (int j = 0; j < pattern.length; ++j) { for (int j = 0; j < from.length; ++j) {
if (buf[i + j] != pattern[j]) { if (buf.get(i + j) != from[j]) {
match = false; match = false;
break; break;
} }
} }
if (match) { // Make sure it is null terminated
if (match && buf.get(i + from.length) == '\0') {
offset = i; offset = i;
break; 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) if (offset < 0)
return false; return false;
byte[] dest = new byte[target.length - 2]; buf.position(offset);
for (int i = 0; i < to.length(); ++i) { buf.put(to);
dest[i * 2] = (byte) to.charAt(i);
}
System.arraycopy(dest, 0, xml, offset, dest.length);
return true; return true;
} }
private static boolean findAndPatch(byte xml[], String from, String to) { private static boolean findAndPatch(byte xml[], int a, int b) {
byte target[] = (from + '\0').getBytes(); IntBuffer buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
int offset = findOffset(xml, target); int len = xml.length / 4;
if (offset < 0) for (int i = 0; i < len; ++i) {
return fallbackPatch(xml, from, to); if (buf.get(i) == a) {
System.arraycopy(to.getBytes(), 0, xml, offset, to.length()); buf.put(i, b);
return true; return true;
}
}
return false;
} }
private static boolean patchAndHide() { private static boolean patchAndHide() {
MagiskManager mm = Data.MM(); MagiskManager mm = Data.MM();
// Generate a new app with random package name // Generate a new app with random package name
SuFile repack = new SuFile("/data/local/tmp/repack.apk"); File repack = new File(mm.getFilesDir(), "patched.apk");
String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length()); String pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length());
try { try {
JarMap apk = new JarMap(mm.getPackageCodePath()); JarMap apk = new JarMap(mm.getPackageCodePath());
if (!patchPackageID(apk, Const.ORIG_PKG_NAME, pkg)) if (!patch(apk, pkg))
return false; return false;
SignAPK.sign(apk, new SuFileOutputStream(repack)); SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(repack)));
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
// Install the application // Install the application
repack.setReadable(true, false);
if (!ShellUtils.fastCmdResult("pm install " + repack)) if (!ShellUtils.fastCmdResult("pm install " + repack))
return false; return false;;
repack.delete();
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg); mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
Data.exportPrefs(); Data.exportPrefs();
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME); RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, pkg);
return true; return true;
} }
public static boolean patchPackageID(JarMap apk, String from, String to) { public static boolean patch(JarMap apk, String pkg) {
try { try {
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST); JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
byte xml[] = apk.getRawData(je); byte xml[] = apk.getRawData(je);
if (!findAndPatch(xml, from, to)) if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) ||
return false; !findAndPatch(xml, BuildConfig.APPLICATION_ID + ".provider", pkg + ".provider") ||
if (!findAndPatch(xml, from + ".provider", to + ".provider")) !findAndPatch(xml, R.string.app_name, R.string.re_app_name))
return false; return false;
// Write in changes // Write in changes
@@ -136,18 +141,16 @@ public class PatchAPK {
return true; return true;
} }
public static void hideManager(Activity activity) { public static void hideManager() {
ProgressDialog dialog = ProgressDialog.show(activity,
activity.getString(R.string.hide_manager_toast),
activity.getString(R.string.hide_manager_toast2));
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { 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(); boolean b = patchAndHide();
Data.mainHandler.post(() -> { mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);
dialog.cancel(); if (!b) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
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.database.Cursor;
import android.os.AsyncTask; 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.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.Repo; import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic; 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.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@@ -22,9 +23,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -32,51 +31,45 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class UpdateRepos { public class UpdateRepos {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 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 int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
private static final DateFormat dateFormat; private static final DateFormat dateFormat;
private MagiskManager mm;
private Set<String> cached;
private ExecutorService threadPool;
static { static {
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
} }
private MagiskManager mm;
private Set<String> cached;
private ExecutorService threadPool;
public UpdateRepos() { public UpdateRepos() {
mm = Data.MM(); mm = Data.MM();
} }
private void waitTasks() { private void waitTasks() {
threadPool.shutdown(); threadPool.shutdown();
try { while (true) {
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); try {
} catch (InterruptedException ignored) {} if (threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS))
break;
} catch (InterruptedException ignored) {}
}
} }
private boolean loadJSON(String jsonString) throws JSONException, ParseException { private void loadJSON(JSONArray array) throws JSONException, ParseException {
JSONArray jsonArray = new JSONArray(jsonString); for (int i = 0; i < array.length(); i++) {
JSONObject rawRepo = array.getJSONObject(i);
// Empty page, halt String id = rawRepo.getString("name");
if (jsonArray.length() == 0)
return false;
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject rawRepo = jsonArray.getJSONObject(i);
String id = rawRepo.getString("description");
String name = rawRepo.getString("name");
Date date = dateFormat.parse(rawRepo.getString("pushed_at")); Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
Set<String> set = Collections.synchronizedSet(cached);
threadPool.execute(() -> { threadPool.execute(() -> {
Repo repo = mm.repoDB.getRepo(id); Repo repo = mm.repoDB.getRepo(id);
try { try {
if (repo == null) if (repo == null)
repo = new Repo(name); repo = new Repo(id);
else else
set.remove(id); cached.remove(id);
repo.update(date); repo.update(date);
mm.repoDB.addRepo(repo); mm.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) { } 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 /* 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 * first page is updated to determine whether the online repo database is changed
*/ */
private boolean loadPage(int page) { private boolean loadPage(int page) {
Map<String, String> header = new HashMap<>(); ANRequest.GetRequestBuilder req = AndroidNetworking.get(Const.Url.REPO_URL)
if (page == 0) .addQueryParameter("page", String.valueOf(page + 1));
header.put(Const.Key.IF_NONE_MATCH, mm.prefs.getString(Const.Key.ETAG_KEY, "")); if (page == 0) {
String url = Utils.fmt(Const.Url.REPO_URL, page + 1); 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 { try {
HttpURLConnection conn = WebService.request(url, header); loadJSON(res.getResult());
// No updates } catch (JSONException | ParseException e) {
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) {
// Should not happen, but if exception occurs, page load fails // Should not happen, but if exception occurs, page load fails
return false; return false;
} }
// Update ETAG // Update ETAG
if (page == 0) { if (page == 0) {
String etag = header.get(Const.Key.ETAG_KEY); String etag = res.getOkHttpResponse().header(Const.Key.ETAG_KEY);
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1); if (etag != null) {
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply(); 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); return links == null || !links.contains("next") || loadPage(page + 1);
} }
private boolean loadPages() {
return loadPage(0);
}
private void fullReload() { private void fullReload() {
Cursor c = mm.repoDB.getRawCursor(); Cursor c = mm.repoDB.getRawCursor();
while (c.moveToNext()) { while (c.moveToNext()) {
@@ -141,10 +142,10 @@ public class UpdateRepos {
public void exec(boolean force) { public void exec(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE); Topic.reset(Topic.REPO_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
cached = mm.repoDB.getRepoIDSet(); cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE); threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
if (loadPage(0)) { if (loadPages()) {
waitTasks(); waitTasks();
// The leftover cached means they are removed from online repo // The leftover cached means they are removed from online repo
mm.repoDB.removeRepo(cached); mm.repoDB.removeRepo(cached);

View File

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

View File

@@ -2,34 +2,37 @@ package com.topjohnwu.magisk.components;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; 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.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.NoUIActivity;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager; import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic; 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; private ActivityResultListener activityResultListener;
static int[] EMPTY_INT_ARRAY = new int[0];
public MagiskManager mm; 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 @Override
public int[] getSubscribedTopics() { public int[] getSubscribedTopics() {
return EMPTY_INT_ARRAY; return EMPTY_INT_ARRAY;
@@ -40,13 +43,25 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
return -1; 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 @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Topic.subscribe(this); Topic.subscribe(this);
if (Data.isDarkTheme && getDarkTheme() != -1) { if (Data.isDarkTheme && getDarkTheme() != -1) {
setTheme(getDarkTheme()); setTheme(getDarkTheme());
} }
super.onCreate(savedInstanceState);
String[] perms = getIntent().getStringArrayExtra(INTENT_PERM);
if (perms != null)
ActivityCompat.requestPermissions(this, perms, 0);
} }
@Override @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 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (activityResultListener != null) if (activityResultListener != null)
@@ -81,6 +124,23 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
super.startActivityForResult(intent, requestCode); 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 { public interface ActivityResultListener {
void onActivityResult(int requestCode, int resultCode, Intent data); void onActivityResult(int requestCode, int resultCode, Intent data);
} }

View File

@@ -1,15 +1,18 @@
package com.topjohnwu.magisk.components; package com.topjohnwu.magisk.components;
import android.content.Intent; import android.content.Intent;
import android.support.v4.app.Fragment;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import androidx.fragment.app.Fragment;
import butterknife.Unbinder;
public class BaseFragment extends Fragment implements Topic.AutoSubscriber { public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
public MagiskManager mm; public MagiskManager mm;
protected Unbinder unbinder = null;
public BaseFragment() { public BaseFragment() {
mm = Data.MM(); mm = Data.MM();
@@ -27,6 +30,13 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
super.onPause(); super.onPause();
} }
@Override
public void onDestroyView() {
super.onDestroyView();
if (unbinder != null)
unbinder.unbind();
}
@Override @Override
public void startActivityForResult(Intent intent, int requestCode) { public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, this::onActivityResult); startActivityForResult(intent, requestCode, this::onActivityResult);
@@ -42,6 +52,6 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
@Override @Override
public int[] getSubscribedTopics() { 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.app.Activity;
import android.content.DialogInterface; 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.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
@@ -15,8 +10,12 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R; 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.BindView;
import butterknife.ButterKnife;
public class CustomAlertDialog extends AlertDialog.Builder { public class CustomAlertDialog extends AlertDialog.Builder {
@@ -37,7 +36,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@BindView(R.id.neutral) public Button neutral; @BindView(R.id.neutral) public Button neutral;
ViewHolder(View v) { ViewHolder(View v) {
ButterKnife.bind(this, v); new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
messageView.setVisibility(View.GONE); messageView.setVisibility(View.GONE);
negative.setVisibility(View.GONE); negative.setVisibility(View.GONE);
positive.setVisibility(View.GONE); positive.setVisibility(View.GONE);

View File

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

View File

@@ -1,22 +1,24 @@
package com.topjohnwu.magisk.components; package com.topjohnwu.magisk.components;
import android.content.Context; import android.Manifest;
import android.app.Activity;
import android.content.Intent; 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 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.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity; import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R; 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.magisk.utils.Utils;
import java.util.List; import java.util.List;
import androidx.appcompat.app.AlertDialog;
class InstallMethodDialog extends AlertDialog.Builder { class InstallMethodDialog extends AlertDialog.Builder {
InstallMethodDialog(BaseActivity activity, List<String> options) { InstallMethodDialog(BaseActivity activity, List<String> options) {
@@ -26,54 +28,78 @@ class InstallMethodDialog extends AlertDialog.Builder {
Intent intent; Intent intent;
switch (idx) { switch (idx) {
case 1: case 1:
if (Data.remoteMagiskVersionCode < 1400) { patchBoot(activity);
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);
}
});
break; break;
case 0: case 0:
String filename = Utils.fmt("Magisk-v%s(%d).zip", downloadOnly(activity);
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
Download.receive(activity, new DownloadReceiver() {
@Override
public void onDownloadDone(Context context, Uri uri) {
SnackbarMaker.showUri(activity, uri);
}
}, Data.magiskLink, filename);
break; break;
case 2: 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); .putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
activity.startActivity(intent); activity.startActivity(intent);
break; break;
case 3: case 3:
new CustomAlertDialog(activity) installInactiveSlot(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();
break; break;
default: 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; package com.topjohnwu.magisk.components;
import android.Manifest;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow; 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 com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.NonNull;
public class ManagerInstallDialog extends CustomAlertDialog { public class ManagerInstallDialog extends CustomAlertDialog {
public ManagerInstallDialog(@NonNull BaseActivity activity) { public ManagerInstallDialog(@NonNull BaseActivity activity) {
super(activity); super(activity);
MagiskManager mm = Data.MM(); 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); Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name))); setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)));
setMessage(mm.getString(R.string.repo_install_msg, filename)); setMessage(mm.getString(R.string.repo_install_msg, name));
setCancelable(true); setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> activity.runWithPermission( setPositiveButton(R.string.install, (d, i) -> DlInstallManager.upgrade(name));
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> { setNegativeButton(R.string.no_thanks, null);
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);
if (!TextUtils.isEmpty(Data.managerNoteLink)) { if (!TextUtils.isEmpty(Data.managerNoteLink)) {
setNeutralButton(R.string.app_changelog, (d, i) -> setNeutralButton(R.string.app_changelog, (d, i) ->
new MarkDownWindow(activity, null, Data.managerNoteLink).exec()); 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.app.Activity;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.StringRes;
public class SnackbarMaker { public class SnackbarMaker {
public static Snackbar make(Activity activity, CharSequence text, int duration) { public static Snackbar make(Activity activity, CharSequence text, int duration) {
@@ -34,7 +35,7 @@ public class SnackbarMaker {
} }
private static void setup(Snackbar snack) { 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); text.setMaxLines(Integer.MAX_VALUE);
} }

View File

@@ -2,28 +2,29 @@ package com.topjohnwu.magisk.components;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.widget.Toast; 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.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity; import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; 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.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import java.io.File;
import androidx.annotation.NonNull;
public class UninstallDialog extends CustomAlertDialog { public class UninstallDialog extends CustomAlertDialog {
public UninstallDialog(@NonNull Activity activity) { public UninstallDialog(@NonNull Activity activity) {
super(activity); super(activity);
MagiskManager mm = Data.MM();
setTitle(R.string.uninstall_magisk_title); setTitle(R.string.uninstall_magisk_title);
setMessage(R.string.uninstall_magisk_msg); setMessage(R.string.uninstall_magisk_msg);
setNeutralButton(R.string.restore_img, (d, i) -> { setNeutralButton(R.string.restore_img, (d, i) -> {
@@ -40,17 +41,29 @@ public class UninstallDialog extends CustomAlertDialog {
}); });
}); });
if (!TextUtils.isEmpty(Data.uninstallerLink)) { if (!TextUtils.isEmpty(Data.uninstallerLink)) {
setPositiveButton(R.string.complete_uninstall, (d, i) -> setPositiveButton(R.string.complete_uninstall, (d, i) -> {
Download.receive(activity, new DownloadReceiver() { 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 @Override
public void onDownloadDone(Context context, Uri uri) { public void onDownloadComplete() {
Intent intent = new Intent(context, FlashActivity.class) progress.dismiss();
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(uri) .setData(Uri.fromFile(zip))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL); .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; package com.topjohnwu.magisk.container;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.support.annotation.NonNull;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull;
public abstract class BaseModule implements Comparable<BaseModule> { 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; private int mVersionCode = -1, minMagiskVersion = -1;
protected BaseModule() {} protected BaseModule() {
mId = mName = mVersion = mAuthor = mDescription = "";
}
protected BaseModule(Cursor c) { protected BaseModule(Cursor c) {
mId = c.getString(c.getColumnIndex("id")); mId = nonNull(c.getString(c.getColumnIndex("id")));
mName = c.getString(c.getColumnIndex("name")); mName = nonNull(c.getString(c.getColumnIndex("name")));
mVersion = c.getString(c.getColumnIndex("version")); mVersion = nonNull(c.getString(c.getColumnIndex("version")));
mVersionCode = c.getInt(c.getColumnIndex("versionCode")); mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
mAuthor = c.getString(c.getColumnIndex("author")); mAuthor = nonNull(c.getString(c.getColumnIndex("author")));
mDescription = c.getString(c.getColumnIndex("description")); mDescription = nonNull(c.getString(c.getColumnIndex("description")));
minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk")); minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk"));
} }
private String nonNull(String s) {
return s == null ? "" : s;
}
public ContentValues getContentValues() { public ContentValues getContentValues() {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put("id", mId); values.put("id", mId);

View File

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

View File

@@ -3,8 +3,10 @@ package com.topjohnwu.magisk.container;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; 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>{ public class Policy implements Comparable<Policy>{
@@ -25,16 +27,16 @@ public class Policy implements Comparable<Policy>{
this.uid = uid; this.uid = uid;
packageName = pkgs[0]; packageName = pkgs[0];
info = pm.getApplicationInfo(packageName, 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 { public Policy(ContentValues values, PackageManager pm) throws PackageManager.NameNotFoundException {
uid = c.getInt(c.getColumnIndex("uid")); uid = values.getAsInteger("uid");
packageName = c.getString(c.getColumnIndex("package_name")); packageName = values.getAsString("package_name");
policy = c.getInt(c.getColumnIndex("policy")); policy = values.getAsInteger("policy");
until = c.getLong(c.getColumnIndex("until")); until = values.getAsInteger("until");
logging = c.getInt(c.getColumnIndex("logging")) != 0; logging = values.getAsInteger("logging") != 0;
notification = c.getInt(c.getColumnIndex("notification")) != 0; notification = values.getAsInteger("notification") != 0;
info = pm.getApplicationInfo(packageName, 0); info = pm.getApplicationInfo(packageName, 0);
if (info.uid != uid) if (info.uid != uid)
throw new PackageManager.NameNotFoundException(); throw new PackageManager.NameNotFoundException();

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,27 +2,21 @@ package com.topjohnwu.magisk.fragments;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.topjohnwu.magisk.Const; import com.google.android.material.tabs.TabLayout;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MainActivity; import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.TabFragmentAdapter; import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
import com.topjohnwu.magisk.components.BaseFragment; import com.topjohnwu.magisk.components.BaseFragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class LogFragment extends BaseFragment { public class LogFragment extends BaseFragment {
private Unbinder unbinder;
@BindView(R.id.container) ViewPager viewPager; @BindView(R.id.container) ViewPager viewPager;
@BindView(R.id.tab) TabLayout tab; @BindView(R.id.tab) TabLayout tab;
@@ -31,15 +25,13 @@ public class LogFragment extends BaseFragment {
Bundle savedInstanceState) { Bundle savedInstanceState) {
// Inflate the layout for this fragment // Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_log, container, false); 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); ((MainActivity) requireActivity()).toolbar.setElevation(0);
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager()); 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)); adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
tab.setupWithViewPager(viewPager); tab.setupWithViewPager(viewPager);
tab.setVisibility(View.VISIBLE); tab.setVisibility(View.VISIBLE);
@@ -48,11 +40,4 @@ public class LogFragment extends BaseFragment {
return v; 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.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; 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.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -41,20 +36,22 @@ import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils; 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.BindColor;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import butterknife.Unbinder;
public class MagiskFragment extends BaseFragment public class MagiskFragment extends BaseFragment
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber { implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
private Container expandableContainer = new Container(); private Container expandableContainer = new Container();
private Unbinder unbinder;
private static boolean shownDialog = false; 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; @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.red500) int colorBad;
@BindColor(R.color.green500) int colorOK; @BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn; @BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.grey500) int colorNeutral; @BindColor(R.color.green500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo; @BindColor(R.color.blue500) int colorInfo;
@OnClick(R.id.safetyNet_title) @OnClick(R.id.safetyNet_title)
@@ -136,7 +133,7 @@ public class MagiskFragment extends BaseFragment
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_magisk, container, false); 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); requireActivity().setTitle(R.string.magisk);
expandableContainer.expandLayout = expandLayout; expandableContainer.expandLayout = expandLayout;
@@ -196,12 +193,6 @@ public class MagiskFragment extends BaseFragment
} }
} }
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override @Override
public Container getContainer() { public Container getContainer() {
return expandableContainer; return expandableContainer;
@@ -223,11 +214,10 @@ public class MagiskFragment extends BaseFragment
boolean hasNetwork = Download.checkNetworkStatus(mm); boolean hasNetwork = Download.checkNetworkStatus(mm);
boolean hasRoot = Shell.rootAccess(); boolean hasRoot = Shell.rootAccess();
boolean isUpToDate = Data.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE); magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
installOptionCard.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); coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
int image, color; int image, color;

View File

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

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.Manifest; import android.Manifest;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@@ -16,11 +14,11 @@ import android.widget.ProgressBar;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseFragment; import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
@@ -28,14 +26,11 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Calendar; import java.util.Calendar;
import androidx.annotation.Nullable;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskLogFragment extends BaseFragment { public class MagiskLogFragment extends BaseFragment {
private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog; @BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog; @BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog; @BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@@ -45,7 +40,7 @@ public class MagiskLogFragment extends BaseFragment {
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false); View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
unbinder = ButterKnife.bind(this, view); unbinder = new MagiskLogFragment_ViewBinding(this, view);
setHasOptionsMenu(true); setHasOptionsMenu(true);
txtLog.setTextIsSelectable(true); txtLog.setTextIsSelectable(true);
return view; return view;
@@ -63,12 +58,6 @@ public class MagiskLogFragment extends BaseFragment {
readLogs(); readLogs();
} }
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu); 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 -> { Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
if (result.getOut().isEmpty()) if (result.getOut().isEmpty())
@@ -103,14 +92,14 @@ public class MagiskLogFragment extends BaseFragment {
}); });
} }
public void saveLogs() { private void saveLogs() {
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log", 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.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); 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 { try {
logFile.createNewFile(); logFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
@@ -121,7 +110,7 @@ public class MagiskLogFragment extends BaseFragment {
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show()); SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
} }
public void clearLogs() { private void clearLogs() {
Shell.su("echo -n > " + Const.MAGISK_LOG).submit(); Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
txtLog.setText(R.string.log_is_empty); txtLog.setText(R.string.log_is_empty);
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); 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.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; 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.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -17,6 +13,7 @@ import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity; import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ModulesAdapter; import com.topjohnwu.magisk.adapters.ModulesAdapter;
@@ -30,19 +27,21 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; 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.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import butterknife.Unbinder;
public class ModulesFragment extends BaseFragment implements Topic.Subscriber { public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView; @BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.empty_rv) TextView emptyRv;
@OnClick(R.id.fab) @OnClick(R.id.fab)
public void selectFile() { void selectFile() {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> { runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip"); intent.setType("application/zip");
@@ -56,7 +55,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_modules, container, false); View view = inflater.inflate(R.layout.fragment_modules, container, false);
unbinder = ButterKnife.bind(this, view); unbinder = new ModulesFragment_ViewBinding(this, view);
setHasOptionsMenu(true); setHasOptionsMenu(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> { mSwipeRefreshLayout.setOnRefreshListener(() -> {
@@ -95,18 +94,12 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) { if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file // 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); intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
startActivity(intent); startActivity(intent);
} }
} }
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_reboot, menu); 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(); Shell.su("/system/bin/reboot bootloader").submit();
return true; return true;
case R.id.reboot_download: case R.id.reboot_download:
Shell.su("/system/bin/reboot download").submit(); Shell.su("/system/bin/reboot upgrade").submit();
return true; return true;
default: default:
return false; return false;

View File

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

View File

@@ -1,85 +1,110 @@
package com.topjohnwu.magisk.fragments; package com.topjohnwu.magisk.fragments;
import android.content.Context; import android.annotation.SuppressLint;
import android.content.SharedPreferences; 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.Build;
import android.os.Bundle; 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.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates; import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.PatchAPK; import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.CustomAlertDialog; import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Download; import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.FingerprintHelper; import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.LocaleManager; import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
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 public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener, implements SharedPreferences.OnSharedPreferenceChangeListener,
Topic.Subscriber, Topic.AutoSubscriber { Topic.Subscriber, Topic.AutoSubscriber {
private PreferenceScreen prefScreen;
private ListPreference updateChannel, suAccess, autoRes, suNotification,
requestTimeout, multiuserMode, namespaceMode;
private MagiskManager mm; 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 @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.app_settings, rootKey);
mm = Data.MM(); 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 magiskCategory = (PreferenceCategory) findPreference("magisk");
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser"); PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
Preference hideManager = findPreference("hide"); Preference hideManager = findPreference("hide");
hideManager.setOnPreferenceClickListener(pref -> {
PatchAPK.hideManager();
return true;
});
Preference restoreManager = findPreference("restore"); 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.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
mm.repoDB.clearRepo(); mm.repoDB.clearRepo();
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT); Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
return true; 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); 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); autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT); requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION); suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE); multiuserConfig = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS); nsConfig = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH); SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT); SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
@@ -109,7 +134,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
// Disable dangerous settings in secondary user // Disable dangerous settings in secondary user
if (Const.USER_ID > 0) { if (Const.USER_ID > 0) {
suCategory.removePreference(multiuserMode); suCategory.removePreference(multiuserConfig);
} }
// Disable re-authentication option on Android O, it will not work // 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); fingerprint.setSummary(R.string.disable_fingerprint);
} }
if (Data.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) { if (Shell.rootAccess() && Const.USER_ID == 0) {
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) { if (mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
hideManager.setOnPreferenceClickListener((pref) -> {
PatchAPK.hideManager(requireActivity());
return true;
});
generalCatagory.removePreference(restoreManager); generalCatagory.removePreference(restoreManager);
} else { } else {
if (Download.checkNetworkStatus(mm)) { 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 {
generalCatagory.removePreference(restoreManager); generalCatagory.removePreference(restoreManager);
}
generalCatagory.removePreference(hideManager); generalCatagory.removePreference(hideManager);
} }
} else { } else {
@@ -165,16 +164,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
generalCatagory.removePreference(hideManager); generalCatagory.removePreference(hideManager);
} }
if (!Shell.rootAccess() || (Const.USER_ID > 0 && if (!showSuperuser) {
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
prefScreen.removePreference(suCategory); prefScreen.removePreference(suCategory);
} }
if (!Shell.rootAccess()) { if (!Shell.rootAccess()) {
prefScreen.removePreference(magiskCategory); prefScreen.removePreference(magiskCategory);
generalCatagory.removePreference(hideManager); 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)); mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
break; break;
} }
Data.loadConfig();
setSummary();
switch (key) { 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: case Const.Key.DARK_THEME:
Topic.publish(false, Topic.RELOAD_ACTIVITY); requireActivity().recreate();
break; break;
case Const.Key.COREONLY: case Const.Key.COREONLY:
if (prefs.getBoolean(key, false)) { if (prefs.getBoolean(key, false)) {
@@ -240,20 +243,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
Shell.su("magiskhide --disable").submit(); Shell.su("magiskhide --disable").submit();
} }
break; 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: case Const.Key.LOCALE:
LocaleManager.setLocale(mm); LocaleManager.setLocale(mm);
Topic.publish(false, Topic.RELOAD_ACTIVITY); requireActivity().recreate();
break; break;
case Const.Key.UPDATE_CHANNEL: case Const.Key.UPDATE_CHANNEL:
case Const.Key.CUSTOM_CHANNEL: case Const.Key.CUSTOM_CHANNEL:
@@ -263,6 +255,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
Utils.setupUpdateCheck(); Utils.setupUpdateCheck();
break; break;
} }
Data.loadConfig();
setSummary();
} }
@Override @Override
@@ -272,54 +266,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
case Const.Key.SU_FINGERPRINT: case Const.Key.SU_FINGERPRINT:
boolean checked = ((SwitchPreference) preference).isChecked(); boolean checked = ((SwitchPreference) preference).isChecked();
((SwitchPreference) preference).setChecked(!checked); ((SwitchPreference) preference).setChecked(!checked);
CustomAlertDialog dialog = new CustomAlertDialog(requireActivity()); FingerprintHelper.showAuthDialog(requireActivity(), () -> {
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder(); ((SwitchPreference) preference).setChecked(checked);
Drawable fingerprint = getResources().getDrawable(R.drawable.ic_fingerprint); mm.mDB.setSettings(key, checked ? 1 : 0);
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);
}
break; break;
} }
return true; return true;
@@ -328,8 +278,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
private void setSummary() { private void setSummary() {
updateChannel.setSummary(getResources() updateChannel.setSummary(getResources()
.getStringArray(R.array.update_channel)[Data.updateChannel]); .getStringArray(R.array.update_channel)[Data.updateChannel]);
suAccess.setSummary(getResources() rootConfig.setSummary(getResources()
.getStringArray(R.array.su_access)[Data.suAccessState]); .getStringArray(R.array.su_access)[rootState]);
autoRes.setSummary(getResources() autoRes.setSummary(getResources()
.getStringArray(R.array.auto_response)[Data.suResponseType]); .getStringArray(R.array.auto_response)[Data.suResponseType]);
suNotification.setSummary(getResources() suNotification.setSummary(getResources()
@@ -337,10 +287,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
requestTimeout.setSummary( requestTimeout.setSummary(
getString(R.string.request_timeout_summary, getString(R.string.request_timeout_summary,
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10"))); mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
multiuserMode.setSummary(getResources() multiuserConfig.setSummary(getResources()
.getStringArray(R.array.multiuser_summary)[Data.multiuserMode]); .getStringArray(R.array.multiuser_summary)[Data.multiuserState]);
namespaceMode.setSummary(getResources() nsConfig.setSummary(getResources()
.getStringArray(R.array.namespace_summary)[Data.suNamespaceMode]); .getStringArray(R.array.namespace_summary)[namespaceState]);
} }
@Override @Override
@@ -352,4 +302,38 @@ public class SettingsFragment extends PreferenceFragmentCompat
public int[] getSubscribedTopics() { public int[] getSubscribedTopics() {
return new int[] {Topic.LOCALE_FETCH_DONE}; 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; package com.topjohnwu.magisk.fragments;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -15,16 +13,15 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.SuLogAdapter; import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.BaseFragment; import com.topjohnwu.magisk.components.BaseFragment;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuLogFragment extends BaseFragment { public class SuLogFragment extends BaseFragment {
@BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView; @BindView(R.id.recyclerView) RecyclerView recyclerView;
private Unbinder unbinder;
private SuLogAdapter adapter; private SuLogAdapter adapter;
@Override @Override
@@ -44,7 +41,7 @@ public class SuLogFragment extends BaseFragment {
Bundle savedInstanceState) { Bundle savedInstanceState) {
// Inflate the layout for this fragment // Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false); View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = ButterKnife.bind(this, v); unbinder = new SuLogFragment_ViewBinding(this, v);
adapter = new SuLogAdapter(mm.mDB); adapter = new SuLogAdapter(mm.mDB);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
@@ -79,10 +76,4 @@ public class SuLogFragment extends BaseFragment {
return true; 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.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -16,24 +14,24 @@ import com.topjohnwu.magisk.container.Policy;
import java.util.List; import java.util.List;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuperuserFragment extends BaseFragment { public class SuperuserFragment extends BaseFragment {
private Unbinder unbinder;
private PackageManager pm;
@BindView(R.id.recyclerView) RecyclerView recyclerView; @BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.empty_rv) TextView emptyRv;
private PackageManager pm;
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false); 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; return view;
} }
@@ -49,14 +47,8 @@ public class SuperuserFragment extends BaseFragment {
displayPolicyList(); displayPolicyList();
} }
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void displayPolicyList() { private void displayPolicyList() {
List<Policy> policyList = mm.mDB.getPolicyList(pm); List<Policy> policyList = mm.mDB.getPolicyList();
if (policyList.size() == 0) { if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE); 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.content.pm.ShortcutManager;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.os.Build; import android.os.Build;
import android.support.annotation.RequiresApi;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity; import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import java.util.ArrayList; import java.util.ArrayList;
import androidx.annotation.RequiresApi;
public class ShortcutReceiver extends BroadcastReceiver { public class ShortcutReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@@ -32,11 +34,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) { private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>(); ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
boolean root = Shell.rootAccess(); boolean root = Shell.rootAccess();
if (root && !(Const.USER_ID > 0 && if (Utils.showSuperUser()) {
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser") shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
.setShortLabel(mm.getString(R.string.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") .putExtra(Const.Key.OPEN_SECTION, "superuser")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -44,11 +45,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
.setRank(0) .setRank(0)
.build()); .build());
} }
if (root && Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide") shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
.setShortLabel(mm.getString(R.string.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") .putExtra(Const.Key.OPEN_SECTION, "magiskhide")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .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) { if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(mm, "modules") shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
.setShortLabel(mm.getString(R.string.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") .putExtra(Const.Key.OPEN_SECTION, "modules")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -67,8 +67,8 @@ public class ShortcutReceiver extends BroadcastReceiver {
.setRank(3) .setRank(3)
.build()); .build());
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads") shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
.setShortLabel(mm.getString(R.string.download)) .setShortLabel(mm.getString(R.string.downloads))
.setIntent(new Intent(mm, SplashActivity.class) .setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "downloads") .putExtra(Const.Key.OPEN_SECTION, "downloads")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))

View File

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

View File

@@ -1,297 +1,21 @@
package com.topjohnwu.magisk.superuser; package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
import android.content.Intent; 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.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.Data;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.BaseActivity; 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 { 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE); Intent intent = new Intent(this, Data.classMap.get(SuRequestActivity.class))
.putExtra("socket", getIntent().getStringExtra("socket"))
pm = getPackageManager(); .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mm.mDB.clearOutdated(); startActivity(intent);
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();
}
finish(); 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.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; 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.utils.SuConnector;
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;
public class SuReceiver extends BroadcastReceiver { public class SuReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
int fromUid, toUid, pid, mode; if (intent != null)
String command, action; SuConnector.handleLogs(intent, 1);
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);
}
} }
} }

View File

@@ -1,12 +1,12 @@
package com.topjohnwu.magisk.utils; package com.topjohnwu.magisk.utils;
import android.support.annotation.Keep;
import com.topjohnwu.utils.SignBoot; import com.topjohnwu.utils.SignBoot;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import androidx.annotation.Keep;
public class BootSigner { public class BootSigner {
@Keep @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; package com.topjohnwu.magisk.utils;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity;
import android.app.KeyguardManager; 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.hardware.fingerprint.FingerprintManager;
import android.os.Build; import android.os.Build;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties; import android.security.keystore.KeyProperties;
import android.view.Gravity;
import android.widget.Toast;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import java.security.KeyStore; import java.security.KeyStore;
@@ -26,6 +35,16 @@ public abstract class FingerprintHelper {
private Cipher cipher; private Cipher cipher;
private CancellationSignal cancel; 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() { public static boolean canUseFingerprint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return false; return false;
@@ -35,6 +54,56 @@ public abstract class FingerprintHelper {
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints(); 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 { protected FingerprintHelper() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = Data.MM().getSystemService(FingerprintManager.class); manager = Data.MM().getSystemService(FingerprintManager.class);

View File

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

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.utils;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.StringRes;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
@@ -16,6 +15,8 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import androidx.annotation.StringRes;
public class LocaleManager { public class LocaleManager {
public static Locale locale = Locale.getDefault(); public static Locale locale = Locale.getDefault();
public final static Locale defaultLocale = 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; package com.topjohnwu.magisk.utils;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
@@ -14,6 +13,8 @@ import com.topjohnwu.superuser.io.SuFile;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import androidx.annotation.NonNull;
public class RootUtils extends Shell.Initializer { public class RootUtils extends Shell.Initializer {
static { static {
@@ -24,32 +25,27 @@ public class RootUtils extends Shell.Initializer {
Shell.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg).exec(); 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 @Override
public boolean onInit(Context context, @NonNull Shell shell) { public boolean onInit(Context context, @NonNull Shell shell) {
Shell.Job job = shell.newJob(); Shell.Job job = shell.newJob();
if (shell.isRoot()) { if (shell.isRoot()) {
InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions); if (!new SuFile("/sbin/.magisk").exists())
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils); job.add("ln -s /sbin/.core /sbin/.magisk");
job.add(magiskUtils).add(managerUtils);
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"); 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(); Data.loadMagiskInfo();
} else { } else {
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils); InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
job.add(nonroot); 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.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT")); 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; package com.topjohnwu.magisk.utils;
import android.support.annotation.IntDef;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -9,23 +7,24 @@ import java.lang.annotation.RetentionPolicy;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import androidx.annotation.IntDef;
public class Topic { public class Topic {
public static final int MAGISK_HIDE_DONE = 0; public static final int MAGISK_HIDE_DONE = 0;
public static final int RELOAD_ACTIVITY = 1; public static final int MODULE_LOAD_DONE = 1;
public static final int MODULE_LOAD_DONE = 2; public static final int REPO_LOAD_DONE = 2;
public static final int REPO_LOAD_DONE = 3; public static final int UPDATE_CHECK_DONE = 3;
public static final int UPDATE_CHECK_DONE = 4; public static final int SNET_CHECK_DONE = 4;
public static final int SNET_CHECK_DONE = 5; public static final int LOCALE_FETCH_DONE = 5;
public static final int LOCALE_FETCH_DONE = 6;
@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}) UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCALE_FETCH_DONE})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface TopicID {} public @interface TopicID {}
// We will not dynamically add topics, so use arrays instead of hash tables // 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) { public static void subscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) { for (int topic : topics) {

View File

@@ -6,12 +6,17 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; 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.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.widget.Toast; import android.widget.Toast;
import com.androidnetworking.AndroidNetworking;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; 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.Module;
import com.topjohnwu.magisk.container.ValueSortedMap; import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.services.UpdateCheckService; import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFile;
import java.util.Locale; import java.util.Locale;
@@ -77,7 +83,7 @@ public class Utils {
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) { if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() || if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) { 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) JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true) .setPersisted(true)
@@ -113,13 +119,40 @@ public class Utils {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
Map<String, Module> moduleMap = new ValueSortedMap<>(); Map<String, Module> moduleMap = new ValueSortedMap<>();
SuFile path = new SuFile(Const.MAGISK_PATH); SuFile path = new SuFile(Const.MAGISK_PATH);
String[] modules = path.list( SuFile[] modules = path.listFiles(
(file, name) -> !name.equals("lost+found") && !name.equals(".core")); (file, name) -> !name.equals("lost+found") && !name.equals(".core"));
for (String name : modules) { for (SuFile file : modules) {
Module module = new Module(Const.MAGISK_PATH + "/" + name); if (file.isFile()) continue;
Module module = new Module(Const.MAGISK_PATH + "/" + file.getName());
moduleMap.put(module.getId(), module); moduleMap.put(module.getId(), module);
} }
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap); 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:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<android.support.v7.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@@ -54,42 +54,42 @@
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/> android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
</LinearLayout> </LinearLayout>
<com.topjohnwu.magisk.components.AboutCardRow <a.l
android:id="@+id/app_version_info" android:id="@+id/app_version_info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:icon="@drawable/ic_info_outline" app:icon="@drawable/ic_info_outline"
app:text="@string/app_version"/> app:text="@string/app_version"/>
<com.topjohnwu.magisk.components.AboutCardRow <a.l
android:id="@+id/app_changelog" android:id="@+id/app_changelog"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:icon="@drawable/ic_history" app:icon="@drawable/ic_history"
app:text="@string/app_changelog"/> app:text="@string/app_changelog"/>
<com.topjohnwu.magisk.components.AboutCardRow <a.l
android:id="@+id/app_translators" android:id="@+id/app_translators"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:icon="@drawable/ic_language" app:icon="@drawable/ic_language"
app:text="@string/app_translators"/> app:text="@string/app_translators"/>
<com.topjohnwu.magisk.components.AboutCardRow <a.l
android:id="@+id/follow_twitter" android:id="@+id/follow_twitter"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:icon="@drawable/ic_twitter" app:icon="@drawable/ic_twitter"
app:text="@string/follow_twitter"/> app:text="@string/follow_twitter"/>
<com.topjohnwu.magisk.components.AboutCardRow <a.l
android:id="@+id/app_source_code" android:id="@+id/app_source_code"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:icon="@drawable/ic_github" app:icon="@drawable/ic_github"
app:text="@string/app_source_code"/> app:text="@string/app_source_code"/>
<com.topjohnwu.magisk.components.AboutCardRow <a.l
android:id="@+id/support_thread" android:id="@+id/support_thread"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -98,7 +98,7 @@
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </androidx.cardview.widget.CardView>
</LinearLayout> </LinearLayout>

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
@@ -24,7 +24,7 @@
</FrameLayout> </FrameLayout>
<android.support.design.widget.NavigationView <com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view" android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -32,4 +32,4 @@
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
app:menu="@menu/drawer" /> 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:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/su_popup" android:id="@+id/su_popup"
tools:context=".superuser.RequestActivity" tools:context=".SuRequestActivity"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:minWidth="350dp" android:minWidth="350dp"

View File

@@ -7,7 +7,7 @@
android:orientation="vertical" android:orientation="vertical"
tools:context="com.topjohnwu.magisk.fragments.LogFragment"> 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
@@ -17,10 +17,10 @@
android:elevation="4dp" android:elevation="4dp"
android:visibility="gone"> 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_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:id="@+id/container"/> android:id="@+id/container"/>

View File

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