diff --git a/docs/allclasses-frame.html b/docs/allclasses-frame.html index f1687440..8bf1fc7c 100644 --- a/docs/allclasses-frame.html +++ b/docs/allclasses-frame.html @@ -2,9 +2,9 @@ - + All Classes (libsu API) - + @@ -12,6 +12,7 @@

All Classes

diff --git a/docs/allclasses-noframe.html b/docs/allclasses-noframe.html index 10815b78..a6d1ba64 100644 --- a/docs/allclasses-noframe.html +++ b/docs/allclasses-noframe.html @@ -2,9 +2,9 @@ - + All Classes (libsu API) - + @@ -12,6 +12,7 @@

All Classes

diff --git a/docs/com/topjohnwu/superuser/BusyBox.html b/docs/com/topjohnwu/superuser/BusyBox.html new file mode 100644 index 00000000..36882df4 --- /dev/null +++ b/docs/com/topjohnwu/superuser/BusyBox.html @@ -0,0 +1,318 @@ + + + + + +BusyBox (libsu API) + + + + + + + + +
+ + +
Skip navigation links
+ + + + +
+ + + +
+
com.topjohnwu.superuser
+

Class BusyBox

+
+
+ +
+ +
+
+ +
+
+ +
+
+ + +
+ + +
Skip navigation links
+ + + + +
+ + + + diff --git a/docs/com/topjohnwu/superuser/CallbackList.html b/docs/com/topjohnwu/superuser/CallbackList.html index 0af5ba86..db3a1438 100644 --- a/docs/com/topjohnwu/superuser/CallbackList.html +++ b/docs/com/topjohnwu/superuser/CallbackList.html @@ -2,9 +2,9 @@ - + CallbackList (libsu API) - + @@ -48,7 +48,7 @@
Parameters:
-
shell - the newly constructed shell
+
shell - the newly constructed shell.
diff --git a/docs/com/topjohnwu/superuser/Shell.Sync.html b/docs/com/topjohnwu/superuser/Shell.Sync.html index d7b29214..87ea1fa5 100644 --- a/docs/com/topjohnwu/superuser/Shell.Sync.html +++ b/docs/com/topjohnwu/superuser/Shell.Sync.html @@ -2,9 +2,9 @@ - + Shell.Sync (libsu API) - + @@ -49,7 +49,7 @@
Parameters:
-
output - the list to store STDOUT outputs.
-
error - the list to store STDERR outputs.
+
outList - the list storing STDOUT outputs. null to ignore outputs.
+
errList - the list storing STDERR outputs. null to ignore outputs.
callback - the callback when all commands are ran and the outputs are done.
commands - the commands to run in the shell.
@@ -891,18 +1062,20 @@

run

@@ -191,10 +198,16 @@

Constructor Summary

SuFile(String pathname)  +SuFile(String pathname, + boolean shell) +
Create a new SuFile using a path.
+ + + SuFile(String parent, String child)  - + SuFile(URI uri)  @@ -249,94 +262,104 @@

Method Summary

getAbsoluteFile()  +String +getAbsolutePath()  + + SuFile getCanonicalFile()  - + +String +getCanonicalPath()  + + long getFreeSpace()  - + SuFile getParentFile()  - + long getTotalSpace()  - + long getUsableSpace()  - + boolean isDirectory()  - + boolean isFile()  - + long lastModified()  - + long -length()  +length() +
Returns the length of the file denoted by this abstract pathname.
+ - + String[] list()  - + String[] list(FilenameFilter filter)  - + SuFile[] listFiles()  - + SuFile[] listFiles(FileFilter filter)  - + SuFile[] listFiles(FilenameFilter filter)  - + boolean mkdir()  - + boolean mkdirs()  - + boolean renameTo(File dest)  - + boolean setExecutable(boolean executable, boolean ownerOnly)  - + boolean setLastModified(long time)
Sets the last-modified time of the file or directory named by this abstract pathname.
- + boolean setReadable(boolean readable, boolean ownerOnly)  - + boolean setReadOnly()  - + boolean setWritable(boolean writable, boolean ownerOnly)  @@ -347,7 +370,7 @@

Method Summary

Methods inherited from class java.io.File

-compareTo, createTempFile, createTempFile, equals, getAbsolutePath, getCanonicalPath, getName, getParent, getPath, hashCode, isAbsolute, isHidden, listRoots, setExecutable, setReadable, setWritable, toPath, toString, toURI, toURL +compareTo, createTempFile, createTempFile, equals, getName, getParent, getPath, hashCode, isAbsolute, isHidden, listRoots, setExecutable, setReadable, setWritable, toPath, toString, toURI, toURL + + + + @@ -562,6 +614,22 @@

getAbsoluteFile

+ + + + @@ -676,9 +744,17 @@

lastModified

  • length

    public long length()
    +
    Returns the length of the file denoted by this abstract pathname. +

    + Note: If there is no blockdev and stat in PATH, the file size is + the value reported from ls -ld, which will not correctly report the size of block files.

    Overrides:
    length in class File
    +
    Returns:
    +
    the size in bytes of the underlying file.
    +
    See Also:
    +
    File.length()
  • @@ -892,7 +968,7 @@

    listFiles

    diff --git a/docs/com/topjohnwu/superuser/io/package-summary.html b/docs/com/topjohnwu/superuser/io/package-summary.html index 5d2ba639..8e2a1fd8 100644 --- a/docs/com/topjohnwu/superuser/io/package-summary.html +++ b/docs/com/topjohnwu/superuser/io/package-summary.html @@ -2,9 +2,9 @@ - + com.topjohnwu.superuser.io (libsu API) - + @@ -87,6 +87,36 @@

    Package com.topjohnwu.superuser.io

    A File implementation with root access.
    + +SuFileInputStream + +
    An InputStream that read files using the global shell instance.
    + + + +SuFileOutputStream + +
    An OutputStream that read files using the global shell instance.
    + + + +SuProcessFileInputStream + +
    An InputStream that read files by opening a new root process.
    + + + +SuProcessFileOutputStream + +
    An OutputStream that write files by opening a new root process.
    + + + +SuRandomAccessFile + +
    Access files using the global shell instance and mimics RandomAccessFile.
    + + diff --git a/docs/com/topjohnwu/superuser/io/package-tree.html b/docs/com/topjohnwu/superuser/io/package-tree.html index 263f20a0..4aa225b6 100644 --- a/docs/com/topjohnwu/superuser/io/package-tree.html +++ b/docs/com/topjohnwu/superuser/io/package-tree.html @@ -2,9 +2,9 @@ - + com.topjohnwu.superuser.io Class Hierarchy (libsu API) - + @@ -85,6 +85,27 @@

    Class Hierarchy

  • com.topjohnwu.superuser.io.SuFile
  • +
  • java.io.InputStream (implements java.io.Closeable) + +
  • +
  • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable) + +
  • +
  • com.topjohnwu.superuser.io.SuRandomAccessFile (implements java.io.Closeable, java.io.DataInput, java.io.DataOutput)
  • diff --git a/docs/com/topjohnwu/superuser/package-frame.html b/docs/com/topjohnwu/superuser/package-frame.html index 60c6c642..79575eff 100644 --- a/docs/com/topjohnwu/superuser/package-frame.html +++ b/docs/com/topjohnwu/superuser/package-frame.html @@ -2,9 +2,9 @@ - + com.topjohnwu.superuser (libsu API) - + @@ -16,15 +16,18 @@

    Interfaces

  • Shell.Async.Callback
  • Shell.Container
  • Shell.GetShellCallback
  • +
  • Shell.Task
  • Classes

    Exceptions

    +
  • com.topjohnwu.superuser.BusyBox
  • android.content.Context
  • +
  • com.topjohnwu.superuser.BusyBox
  • android.content.Context
    • android.content.ContextWrapper @@ -108,10 +109,32 @@

      Class Hierarchy

    • com.topjohnwu.superuser.io.SuFile
  • +
  • java.io.InputStream (implements java.io.Closeable) + +
  • +
  • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable) + +
  • com.topjohnwu.superuser.Shell (implements java.io.Closeable)
  • com.topjohnwu.superuser.Shell.Async
  • com.topjohnwu.superuser.Shell.Initializer
  • com.topjohnwu.superuser.Shell.Sync
  • +
  • com.topjohnwu.superuser.ShellUtils
  • +
  • com.topjohnwu.superuser.io.SuRandomAccessFile (implements java.io.Closeable, java.io.DataInput, java.io.DataOutput)
  • java.lang.Throwable (implements java.io.Serializable) diff --git a/docs/serialized-form.html b/docs/serialized-form.html index 36ef8c78..bec54881 100644 --- a/docs/serialized-form.html +++ b/docs/serialized-form.html @@ -2,9 +2,9 @@ - + Serialized Form (libsu API) - + @@ -94,10 +94,22 @@

    Class

    Serialized Fields

  • diff --git a/superuser/src/main/java/com/topjohnwu/superuser/BusyBox.java b/superuser/src/main/java/com/topjohnwu/superuser/BusyBox.java index f9581fc1..a390d3c4 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/BusyBox.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/BusyBox.java @@ -27,13 +27,61 @@ import java.util.Arrays; import java.util.List; -public class BusyBox { +/** + * A class that handles the bundled BusyBox. + *

    + * {@code libsu} bundles with busybox binaries for both arm and x86 (arm64 and x64 are also covered). + * Developers using {@code libsu} can easily access busybox applets by calling {@link #setup(Context)} + * before any new shell is created (place this in the same place where you call + * {@link Shell#setFlags(int)}) and {@link Shell#setInitializer(Shell.Initializer)}. + * After calling {@link #setup(Context)}, busybox will be installed in the app's internal storage, + * and all new shells created will have the path to busybox prepended to {@code PATH}. + * This makes sure all commands are using the applets from busybox, providing predictable + * behavior so that developers can have less headache handling different implementation of the + * common shell utilities. Some operations in {@link com.topjohnwu.superuser.io} depends on a + * busybox to work properly, check before using them. + *

    + * Note: the busybox binaries will add around 1.6MB to your APK, for developers not willing + * to use the busybox binaries bundled in {@code libsu}, you can use proguard to remove it: + * remember to NOT call {@link #setup(Context)} anywhere in your code, and enable both + * minifyEnabled and shrinkResources in your release builds. For more info, please + * check the official documentation. + */ +public final class BusyBox { + + private BusyBox() {} + /** + * The path pointing to the folder where busybox is installed. + *

    + * If your app would like to rely on external busybox, you can directly assign the path to this field. + * All new shell instances created will have this directory prepended to {@code PATH}. + *

    + * For example: Magisk Manager relies on the Magisk's internal busybox (located in + * {@code /sbin/.core/busybox}). So instead of calling {@link #setup(Context)}, it can directly + * assign the busybox path to this field to discard the bundled busybox binaries. + * ({@code BusyBox.BB_PATH = new File("/sbin/.core/busybox")}). + */ public static File BB_PATH = null; private static final String ARM_MD5 = "9adae2e0993fb6f233cd750f13393695"; private static final String X86_MD5 = "498d1ec0b47e31b58074e533e664bd13"; + /** + * Setup a busybox environment using the bundled busybox binaries. + *

    + * Busybox will be installed to the internal data of the application. On Android 7.0+, it + * will install busybox to Device Protected Storage, so developers willing to support + * Direct Boot + * can use it without an issue. + *

    + * After calling this method, {@link #BB_PATH} will point to the folder where the busybox is + * installed. + *

    + * If you are willing to let proguard to remove the bundled busybox binary to reduce the APK + * size, do NOT call this method. + * @param context a {@link Context} of the current app. + */ public static void setup(Context context) { Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? context.createDeviceProtectedStorageContext() : context; diff --git a/superuser/src/main/java/com/topjohnwu/superuser/ShellUtils.java b/superuser/src/main/java/com/topjohnwu/superuser/ShellUtils.java index 52c26634..4d51adb5 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/ShellUtils.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/ShellUtils.java @@ -34,6 +34,9 @@ import java.util.ArrayList; import java.util.List; +/** + * Some handy utility methods that are used in {@code libsu}. + */ public final class ShellUtils { private ShellUtils() {} @@ -43,6 +46,11 @@ private ShellUtils() {} private static final String NUMBERS = "0123456789"; private static final String ALPHANUM = LOWER_CASE + UPPER_CASE + NUMBERS; + /** + * Generate a random string containing only alphabet and numbers. + * @param length the length of the desired random string. + * @return the random string. + */ public static CharSequence genRandomAlphaNumString(int length) { SecureRandom random = new SecureRandom(); StringBuilder builder = new StringBuilder(); @@ -52,6 +60,11 @@ public static CharSequence genRandomAlphaNumString(int length) { return builder; } + /** + * Test whether the list is {@code null} or empty or all elements are empty strings. + * @param out the output of a shell command. + * @return {@code false} if the list is {@code null} or empty or all elements are empty strings. + */ public static boolean isValidOutput(List out) { if (out != null && out.size() != 0) { // Check if all empty @@ -62,6 +75,12 @@ public static boolean isValidOutput(List out) { return false; } + /** + * Run a single line command and get a single line output. + * @param shell a shell instance. + * @param cmd the single line command. + * @return the last line of the output of the command, {@code null} if no output is available. + */ @Nullable public static String fastCmd(Shell shell, String cmd) { ArrayList out = new ArrayList<>(); @@ -69,10 +88,23 @@ public static String fastCmd(Shell shell, String cmd) { return isValidOutput(out) ? out.get(out.size() - 1) : null; } + /** + * Run a single line command and return whether the command returns 0 (success). + * @param shell a shell instance. + * @param cmd the single line command. + * @return {@code true} if the command succeed. + */ public static boolean fastCmdResult(Shell shell, String cmd) { return Boolean.parseBoolean(fastCmd(shell, cmd + " >/dev/null 2>&1 && echo true || echo false")); } + /** + * Pump all data from an {@link InputStream} to an {@link OutputStream}. + * @param in source. + * @param out target. + * @return the total bytes transferred. + * @throws IOException when any read/write operations throws an error. + */ public static long pump(InputStream in, OutputStream out) throws IOException { int read; long total = 0; @@ -85,7 +117,14 @@ public static long pump(InputStream in, OutputStream out) throws IOException { return total; } - public static boolean checkSum(String alg, File file, String test) { + /** + * Check the checksum of a file using a specific algorithm and compare it with a reference. + * @param alg the algorithm name used in {@link MessageDigest#getInstance(String)}. + * @param file the file to be tested. + * @param reference the reference checksum. + * @return {@code true} if the file's checksum matches reference. + */ + public static boolean checkSum(String alg, File file, String reference) { // Verify checksum try (FileInputStream in = new FileInputStream(file)) { MessageDigest digest = MessageDigest.getInstance(alg); @@ -101,16 +140,24 @@ public void write(@NonNull byte[] b, int off, int len) throws IOException {} for (byte b : chksum) { sb.append(String.format("%02x", b & 0xff)); } - return TextUtils.equals(sb, test); + return TextUtils.equals(sb, reference); } catch (NoSuchAlgorithmException | IOException e) { return false; } } + /** + * Check if current thread is main thread. + * @return {@code true} if the current thread is the main thread. + */ public static boolean onMainThread() { return ((Looper.myLooper() != null) && (Looper.myLooper() == Looper.getMainLooper())); } + /** + * Discard all data currently available in an {@link InputStream}. + * @param in the {@link InputStream} to be cleaned. + */ public static void cleanInputStream(InputStream in) { try { while (in.available() != 0) @@ -118,10 +165,22 @@ public static void cleanInputStream(InputStream in) { } catch (IOException ignored) {} } + /** + * Same as {@code readFully(in, b, 0, b.length)} + */ public static void readFully(InputStream in, byte[] b) throws IOException { readFully(in, b, 0, b.length); } + /** + * Read exactly len bytes from the {@link InputStream}. + * @param in source. + * @param b the byte array to store the data. + * @param off the start offset in the data. + * @param len the number of bytes to be read. + * @throws EOFException if this stream reaches the end before reading all the bytes. + * @throws IOException if an I/O error occurs. + */ public static void readFully(InputStream in, byte[] b, int off, int len) throws IOException { int n = 0; while (n < len) { @@ -132,6 +191,12 @@ public static void readFully(InputStream in, byte[] b, int off, int len) throws } } + /** + * Get the greatest common divisor of 2 integers with binary algorithm. + * @param u an integer. + * @param v an integer. + * @return the greatest common divisor. + */ public static long gcd(long u, long v) { if (u == 0) return v; if (v == 0) return u; diff --git a/superuser/src/main/java/com/topjohnwu/superuser/internal/RandomAccessFileWrapper.java b/superuser/src/main/java/com/topjohnwu/superuser/internal/RandomAccessFileWrapper.java index 3f0a0d90..84c70340 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/internal/RandomAccessFileWrapper.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/internal/RandomAccessFileWrapper.java @@ -64,7 +64,7 @@ public void write(byte[] b) throws IOException { } @Override - public void write(byte[] b, int off, int len) throws IOException { + public void write(@NonNull byte[] b, int off, int len) throws IOException { raf.write(b, off, len); } diff --git a/superuser/src/main/java/com/topjohnwu/superuser/internal/ShellFileIO.java b/superuser/src/main/java/com/topjohnwu/superuser/internal/ShellFileIO.java index 76e949f5..a9b2a24c 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/internal/ShellFileIO.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/internal/ShellFileIO.java @@ -16,6 +16,8 @@ package com.topjohnwu.superuser.internal; +import android.support.annotation.NonNull; + import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.ShellUtils; import com.topjohnwu.superuser.io.SuFile; @@ -53,7 +55,7 @@ class ShellFileIO extends SuRandomAccessFile implements DataInputImpl, DataOutpu } @Override - public void write(byte[] b, int off, int len) throws IOException { + public void write(@NonNull byte[] b, int off, int len) throws IOException { if (off < 0 || len < 0 || off + len > b.length) throw new IndexOutOfBoundsException(); Throwable t = Shell.getShell().execTask((in, out, err) -> { diff --git a/superuser/src/main/java/com/topjohnwu/superuser/io/SuFile.java b/superuser/src/main/java/com/topjohnwu/superuser/io/SuFile.java index f0e33a59..7e7fe057 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/io/SuFile.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/io/SuFile.java @@ -301,7 +301,7 @@ public long lastModified() { * Returns the length of the file denoted by this abstract pathname. *

    * Note: If there is no {@code blockdev} and {@code stat} in {@code PATH}, the file size is - * the value reported from {@code ls -l}, which will not correctly report the size of block files. + * the value reported from {@code ls -ld}, which will not correctly report the size of block files. * @return the size in bytes of the underlying file. * @see File#length() */ diff --git a/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileInputStream.java b/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileInputStream.java index 5bda2391..fdb29bff 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileInputStream.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileInputStream.java @@ -24,12 +24,27 @@ import java.io.FileNotFoundException; import java.io.FilterInputStream; +/** + * An {@link java.io.InputStream} that read files using the global shell instance. + *

    + * This class always checks whether using a shell is necessary. If not, it simply opens a new + * {@link FileInputStream}. + *

    + * Note: this class is always buffered internally, do not add another layer of + * {@link BufferedInputStream} to add more overhead! + */ public class SuFileInputStream extends FilterInputStream { + /** + * @see FileInputStream#FileInputStream(String) + */ public SuFileInputStream(String path) throws FileNotFoundException { this(new SuFile(path)); } + /** + * @see FileInputStream#FileInputStream(File) + */ public SuFileInputStream(File file) throws FileNotFoundException { super(null); SuFile f; diff --git a/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileOutputStream.java b/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileOutputStream.java index 2d4d62ef..dc08d3a7 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileOutputStream.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/io/SuFileOutputStream.java @@ -27,20 +27,41 @@ import java.io.FilterOutputStream; import java.io.IOException; +/** + * An {@link java.io.OutputStream} that read files using the global shell instance. + *

    + * This class always checks whether using a shell is necessary. If not, it simply opens a new + * {@link FileOutputStream}. + *

    + * Note: this class is always buffered internally, do not add another layer of + * {@link BufferedOutputStream} to add more overhead! + */ public class SuFileOutputStream extends FilterOutputStream { + /** + * @see FileOutputStream#FileOutputStream(String) + */ public SuFileOutputStream(String path) throws FileNotFoundException { this(path, false); } + /** + * @see FileOutputStream#FileOutputStream(String, boolean) + */ public SuFileOutputStream(String path, boolean append) throws FileNotFoundException { - this(new File(path), append); + this(new SuFile(path), append); } + /** + * @see FileOutputStream#FileOutputStream(File) + */ public SuFileOutputStream(File file) throws FileNotFoundException { this(file, false); } + /** + * @see FileOutputStream#FileOutputStream(File, boolean) + */ public SuFileOutputStream(File file, boolean append) throws FileNotFoundException { super(null); SuFile f; diff --git a/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileInputStream.java b/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileInputStream.java index 8c4fd853..07a716a6 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileInputStream.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileInputStream.java @@ -19,18 +19,36 @@ import com.topjohnwu.superuser.ShellUtils; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FilterInputStream; import java.io.IOException; +/** + * An {@link java.io.InputStream} that read files by opening a new root process. + *

    + * The difference between this class and {@link SuFileInputStream} is that this class does not + * use a shell to do the I/O operations; instead it directly creates a new process to read the file. + * In most cases, {@link SuFileInputStream} is sufficient; however if you expect a very high I/O + * throughput (e.g. dumping large partitions), this class is created for this purpose. + *

    + * Note: this class is always buffered internally, do not add another layer of + * {@link java.io.BufferedInputStream} to add more overhead! + */ public class SuProcessFileInputStream extends FilterInputStream { private Process process; + /** + * @see FileInputStream#FileInputStream(String) + */ public SuProcessFileInputStream(String path) throws FileNotFoundException { this(new File(path)); } + /** + * @see FileInputStream#FileInputStream(File) + */ public SuProcessFileInputStream(File file) throws FileNotFoundException { super(null); try { diff --git a/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileOutputStream.java b/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileOutputStream.java index d1209e0d..03a2b9b5 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileOutputStream.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/io/SuProcessFileOutputStream.java @@ -22,26 +22,50 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; +/** + * An {@link java.io.OutputStream} that write files by opening a new root process. + *

    + * The difference between this class and {@link SuFileOutputStream} is that this class does not + * use a shell to do the I/O operations; instead it directly creates a new process to write the file. + * In most cases, {@link SuFileOutputStream} is sufficient; however if you expect a very high I/O + * throughput (e.g. restoring large partitions), this class is created for this purpose. + *

    + * Note: this class is always buffered internally, do not add another layer of + * {@link java.io.BufferedOutputStream} to add more overhead! + */ public class SuProcessFileOutputStream extends FilterOutputStream { private Process process; + /** + * @see FileOutputStream#FileOutputStream(String) + */ public SuProcessFileOutputStream(String path) throws FileNotFoundException { this(path, false); } + /** + * @see FileOutputStream#FileOutputStream(String, boolean) + */ public SuProcessFileOutputStream(String path, boolean append) throws FileNotFoundException { this(new File(path), append); } + /** + * @see FileOutputStream#FileOutputStream(File) + */ public SuProcessFileOutputStream(File file) throws FileNotFoundException { this(file, false); } + /** + * @see FileOutputStream#FileOutputStream(File, boolean) + */ public SuProcessFileOutputStream(File file, boolean append) throws FileNotFoundException { super(null); try { diff --git a/superuser/src/main/java/com/topjohnwu/superuser/io/SuRandomAccessFile.java b/superuser/src/main/java/com/topjohnwu/superuser/io/SuRandomAccessFile.java index d556c3f4..00fdd747 100644 --- a/superuser/src/main/java/com/topjohnwu/superuser/io/SuRandomAccessFile.java +++ b/superuser/src/main/java/com/topjohnwu/superuser/io/SuRandomAccessFile.java @@ -28,6 +28,23 @@ import java.io.FileNotFoundException; import java.io.IOException; +/** + * Access files using the global shell instance and mimics {@link java.io.RandomAccessFile}. + *

    + * This class always checks whether using a shell is necessary. If not, it simply opens a new + * {@link java.io.RandomAccessFile} with mode {@code "rw"} and behaves as a wrapper. + *

    + * File random access via shell is extremely limited, each I/O operation comes with a relatively + * large overhead. For optimal performance, please consider using {@link SuFileInputStream} and + * {@link SuFileOutputStream}, since these classes are specifically optimized for I/O using + * shell commands. + *

    + * Note: All write/writeXXX commands require {@code busybox} to work properly, as currently + * no existing Android version ships with a command {@code dd} that supports {@code notrunc} option. + * If you need root file output but unwilling to use {@code busybox}, please use + * {@link SuFileOutputStream} as it uses a special workaround that does not require {@code busybox}. + * @see java.io.RandomAccessFile + */ public abstract class SuRandomAccessFile implements DataInput, DataOutput, Closeable { public static SuRandomAccessFile open(String path) throws FileNotFoundException {