- On Android 5.0 and higher (API 21+), both `SuFileInputStream.open(...)` and `SuFileOutputStream.open(...)` return I/O streams backed by FIFO (named pipes). This provides 100% native file I/O stream performance and stability.
- On Android 4.4 and lower, `SuFileInputStream.open(...)` uses the old shell command backed `InputStream` as it was stress tested and proven reliable.
- On Android 4.4 and lower, `SuFileOutputStream.open(...)` will write all data to a temporary file in the application's cache folder, and will only actually output the data to the target location when the stream is closed. Please refer to Javadocs for more detail.
- If the internal copying of `SuFileOutputStream.open(...)` is unacceptable, `SuFileOutputStream.openNoCopy(...)` can be used to force the old implementation (shell command backed `OutputStream`) on Android 4.4 and lower. **However, according to stress test results, this implementation is error prone and I strongly recommend against using it.**
- If your `minSdkVersion` is 21 or higher (which most apps now are), these I/O stream changes basically improve performance and reliability for free without any complexities mentioned above.
- Fix unaligned shell input (the bug did not affect `SuFileInputStream`)
-`SuFile` now properly escapes special characters of file names in internal implementations
### Deprecation
- Deprecated APIs in 3.0.x is removed
- Creating instances of `SuFileInputStream` and `SuFileOutputStream` is deprecated. Please use the static `SuFileInputStream.open(...)` and `SuFileOutputStream.open(...)` methods instead.
-`SuFile` will now follow symbolic links (as it always should)
-`SuFile.length()` will no longer report block total size on block devices (as it shouldn't in the first place)
-`SuFileInputStream` and `SuFileOutputStream` now supports I/O on all file formats, including character files (e.g. files in `/sys` and `/proc`), and also outputs to block devices correctly
### API Changes
- Make `SuFile.isBlock()`, `SuFile.isCharacter()`, `SuFile.isSymlink()` public
### Improvements
- Tons internal optimizations to improve performance
-`SuFile` is no longer a wrapper class around `File`. Calling the constructor directly will directly open a shell backed `File` instance. This change is due to the fact that `File` wrapper class causes some issues in methods like `File.renameTo(File)`.
### API Changes
- Introduce new helper methods: `SuFile.open(...)`: depending on whether the global shell has root access or not, these methods will return either a normal `File` instance or `SuFile`. This is the same behavior as the previous `SuFile` constructor (the only difference is that `SuFile` used to wrap around the instance).
- When using high level APIs (`Shell.su`, `Shell.sh`, `Shell.rootAccess()`), `NoShellException` is now silently suppressed to prevent unexpected crashes. Should no shell is possible to be created, `Shell.su/sh` will immediately return a `Shell.Result` with no output and show failure; `Shell.rootAccess()` will simply return `false`.
Starting from this release, `libsu` is modularized into 3 parts: `core`, `io`, and `busybox`.
If you only use the shell implementation, you just need `com.github.topjohnwu.libsu:core`. `com.github.topjohnwu.libsu:io` and `com.github.topjohnwu.libsu:busybox` are optional: include the former to use the I/O wrapper classes, and the latter to bundle the prebuilt busybox binaries with your app. The old `com.github.topjohnwu:libsu` can still be used, but all 3 components will be pulled in.
- Add support for multiple `Shell.Initializer`s: new methods `void Shell.Config.setInitializers(...)` and `void Shell.Config.addInitializers(...)` are added; `void Shell.Config.setInitializer(Initializer.class)` is deprecated.
- Remove the class `BusyBox`. To install the prebuilt busybox, add `com.github.topjohnwu.libsu:busybox` as a dependency, and register `BusyBoxInstaller` as an initializer (`Shell.Config.addInitializers(BusyBoxInstaller.class);`)
- Introduce a new flag: `Shell.FLAG_USE_MAGISK_BUSYBOX`. With this flag set, `/sbin/.magisk/busybox` will be prepended to `PATH`, so the shell will use Magisk's internal busybox.
- When creating `SuFileInputStream`/`SuFileOutputStream`/`SuRandomAccessFile` with a character file, it will throw `FileNotFoundException` instead of failing silently. Character file I/O is not possible with shells, use `Shell.su("cat <chr_file>").exec().getOut()` instead.
- Add `boolean Shell.waitAndClose(long, TimeUnit)` and `void Shell.waitAndClose()`. You can now wait for all tasks to finish before closing the shell instance.
- Add `Shell.Config.setTimeout(long)`. Use it to set the maximum time for waiting a new shell construction. The default value is 20 seconds.
-`Shell.isAlive()` method is updated to get process status directly via reflection rather than the traditional `Process.exitValue()` exception handling. This optimization significantly reduces the overhead when switching between Shell tasks.
- Calling `submit` in `PendingJob` used to end up calling the overridden `exec` instead of `exec` in `JobImpl`. Proxy through a private method in `JobImpl` to prevent changed behavior of `JobImpl` subclasses.
- Even though the parameters of `Shell.Job.add(...)` is labeled `@NonNull`, it is still possible that developers still pass in `null` and cause NPE. Check `null` before proceed any further.
- Fix a bug that constructed shell instances weren't cached in the container when created using fallback methods (e.g. no root -> fallback to non-root shell). This would cause infinite loop when no root is available.