Merge "Some small binder shell command improvements."
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 1af2034..d73deb6 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -468,6 +468,9 @@
     /**
      * Handle a call to {@link #shellCommand}.  The default implementation simply prints
      * an error message.  Override and replace with your own.
+     * <p class="caution">Note: no permission checking is done before calling this method; you must
+     * apply any security checks as appropriate for the command being executed.
+     * Consider using {@link ShellCommand} to help in the implementation.</p>
      * @hide
      */
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 6f12b62..54d1090 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -24,9 +24,11 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 
 /**
+ * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}.
  * @hide
  */
 public abstract class ShellCommand {
@@ -44,6 +46,10 @@
     private int mArgPos;
     private String mCurArgData;
 
+    private FileInputStream mFileIn;
+    private FileOutputStream mFileOut;
+    private FileOutputStream mFileErr;
+
     private FastPrintWriter mOutPrintWriter;
     private FastPrintWriter mErrPrintWriter;
     private InputStream mInputStream;
@@ -59,8 +65,12 @@
         mCmd = null;
         mArgPos = firstArgPos;
         mCurArgData = null;
+        mFileIn = null;
+        mFileOut = null;
+        mFileErr = null;
         mOutPrintWriter = null;
         mErrPrintWriter = null;
+        mInputStream = null;
     }
 
     public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
@@ -112,28 +122,65 @@
         return res;
     }
 
+    /**
+     * Return direct raw access (not buffered) to the command's output data stream.
+     */
+    public OutputStream getRawOutputStream() {
+        if (mFileOut == null) {
+            mFileOut = new FileOutputStream(mOut);
+        }
+        return mFileOut;
+    }
+
+    /**
+     * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}.
+     */
     public PrintWriter getOutPrintWriter() {
         if (mOutPrintWriter == null) {
-            FileOutputStream fout = new FileOutputStream(mOut);
-            mOutPrintWriter = new FastPrintWriter(fout);
+            mOutPrintWriter = new FastPrintWriter(getRawOutputStream());
         }
         return mOutPrintWriter;
     }
 
+    /**
+     * Return direct raw access (not buffered) to the command's error output data stream.
+     */
+    public OutputStream getRawErrorStream() {
+        if (mFileErr == null) {
+            mFileErr = new FileOutputStream(mErr);
+        }
+        return mFileErr;
+    }
+
+    /**
+     * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}.
+     */
     public PrintWriter getErrPrintWriter() {
         if (mErr == null) {
             return getOutPrintWriter();
         }
         if (mErrPrintWriter == null) {
-            FileOutputStream fout = new FileOutputStream(mErr);
-            mErrPrintWriter = new FastPrintWriter(fout);
+            mErrPrintWriter = new FastPrintWriter(getRawErrorStream());
         }
         return mErrPrintWriter;
     }
 
-    public InputStream getInputStream() {
+    /**
+     * Return direct raw access (not buffered) to the command's input data stream.
+     */
+    public InputStream getRawInputStream() {
+        if (mFileIn == null) {
+            mFileIn = new FileInputStream(mIn);
+        }
+        return mFileIn;
+    }
+
+    /**
+     * Return buffered access to the command's {@link #getRawInputStream()}.
+     */
+    public InputStream getBufferedInputStream() {
         if (mInputStream == null) {
-            mInputStream = new BufferedInputStream(new FileInputStream(mIn));
+            mInputStream = new BufferedInputStream(getRawInputStream());
         }
         return mInputStream;
     }
@@ -214,7 +261,28 @@
         return -1;
     }
 
+    /**
+     * Implement parsing and execution of a command.  If it isn't a command you understand,
+     * call {@link #handleDefaultCommands(String)} and return its result as a last resort.
+     * User {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
+     * to process additional command line arguments.  Command output can be written to
+     * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
+     *
+     * <p class="caution">Note that no permission checking has been done before entering this function,
+     * so you need to be sure to do your own security verification for any commands you
+     * are executing.  The easiest way to do this is to have the ShellCommand contain
+     * only a reference to your service's aidl interface, and do all of your command
+     * implementations on top of that -- that way you can rely entirely on your executing security
+     * code behind that interface.</p>
+     *
+     * @param cmd The first command line argument representing the name of the command to execute.
+     * @return Return the command result; generally 0 or positive indicates success and
+     * negative values indicate error.
+     */
     public abstract int onCommand(String cmd);
 
+    /**
+     * Implement this to print help text about your command to {@link #getOutPrintWriter()}.
+     */
     public abstract void onHelp();
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 901749e..43e4b84 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -863,7 +863,7 @@
             if (inPath != null) {
                 in = new FileInputStream(inPath);
             } else {
-                in = new SizedInputStream(getInputStream(), sizeBytes);
+                in = new SizedInputStream(getRawInputStream(), sizeBytes);
             }
             out = session.openWrite(splitName, 0, sizeBytes);