Implement shell commands for battery and activity services.

The battery service just implements the existing commands that
are available through dump.

The activity service implements the small set of commands that
are available through dump (not the rest of the dump commands),
and also introduces some of the simple "am" shell commands as
a proof-of-concept of moving those into the service implementation.

Change-Id: If5ff80930dde787703e2682e43c36ce1dab05d69
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 2eeaec9..61fe62f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -19,6 +19,8 @@
 import android.database.ContentObserver;
 import android.os.BatteryStats;
 
+import android.os.ResultReceiver;
+import android.os.ShellCommand;
 import com.android.internal.app.IBatteryStats;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
@@ -96,7 +98,6 @@
     // discharge stats before the device dies.
     private int mCriticalBatteryLevel;
 
-    private static final int DUMP_MAX_LENGTH = 24 * 1024;
     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
 
     private static final String DUMPSYS_DATA_PATH = "/data/system/";
@@ -106,6 +107,7 @@
 
     private final Context mContext;
     private final IBatteryStats mBatteryStats;
+    BinderService mBinderService;
     private final Handler mHandler;
 
     private final Object mLock = new Object();
@@ -162,7 +164,18 @@
 
         // watch for invalid charger messages if the invalid_charger switch exists
         if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
-            mInvalidChargerObserver.startObserving(
+            UEventObserver invalidChargerObserver = new UEventObserver() {
+                @Override
+                public void onUEvent(UEvent event) {
+                    final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
+                    synchronized (mLock) {
+                        if (mInvalidCharger != invalidCharger) {
+                            mInvalidCharger = invalidCharger;
+                        }
+                    }
+                }
+            };
+            invalidChargerObserver.startObserving(
                     "DEVPATH=/devices/virtual/switch/invalid_charger");
         }
     }
@@ -178,7 +191,8 @@
             // Should never happen.
         }
 
-        publishBinderService("battery", new BinderService());
+        mBinderService = new BinderService();
+        publishBinderService("battery", mBinderService);
         publishLocalService(BatteryManagerInternal.class, new LocalService());
     }
 
@@ -593,7 +607,6 @@
             } catch (NumberFormatException e) {
                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
                         durationThresholdString + " or " + dischargeThresholdString);
-                return;
             }
         }
     }
@@ -616,7 +629,133 @@
         }
     }
 
-    private void dumpInternal(PrintWriter pw, String[] args) {
+    class Shell extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            return onShellCommand(this, cmd);
+        }
+
+        @Override
+        public void onHelp() {
+            PrintWriter pw = getOutPrintWriter();
+            dumpHelp(pw);
+        }
+    }
+
+    static void dumpHelp(PrintWriter pw) {
+        pw.println("Battery service (battery) commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println("  set [ac|usb|wireless|status|level|invalid] <value>");
+        pw.println("    Force a battery property value, freezing battery state.");
+        pw.println("  unplug");
+        pw.println("    Force battery unplugged, freezing battery state.");
+        pw.println("  reset");
+        pw.println("    Unfreeze battery state, returning to current hardware values.");
+    }
+
+    int onShellCommand(Shell shell, String cmd) {
+        if (cmd == null) {
+            return shell.handleDefaultCommands(cmd);
+        }
+        PrintWriter pw = shell.getOutPrintWriter();
+        switch (cmd) {
+            case "unplug": {
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, null);
+                if (!mUpdatesStopped) {
+                    mLastBatteryProps.set(mBatteryProps);
+                }
+                mBatteryProps.chargerAcOnline = false;
+                mBatteryProps.chargerUsbOnline = false;
+                mBatteryProps.chargerWirelessOnline = false;
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    mUpdatesStopped = true;
+                    processValuesLocked(false);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } break;
+            case "set": {
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, null);
+                final String key = shell.getNextArg();
+                if (key == null) {
+                    pw.println("No property specified");
+                    return -1;
+
+                }
+                final String value = shell.getNextArg();
+                if (value == null) {
+                    pw.println("No value specified");
+                    return -1;
+
+                }
+                try {
+                    if (!mUpdatesStopped) {
+                        mLastBatteryProps.set(mBatteryProps);
+                    }
+                    boolean update = true;
+                    switch (key) {
+                        case "ac":
+                            mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
+                            break;
+                        case "usb":
+                            mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
+                            break;
+                        case "wireless":
+                            mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
+                            break;
+                        case "status":
+                            mBatteryProps.batteryStatus = Integer.parseInt(value);
+                            break;
+                        case "level":
+                            mBatteryProps.batteryLevel = Integer.parseInt(value);
+                            break;
+                        case "invalid":
+                            mInvalidCharger = Integer.parseInt(value);
+                            break;
+                        default:
+                            pw.println("Unknown set option: " + key);
+                            update = false;
+                            break;
+                    }
+                    if (update) {
+                        long ident = Binder.clearCallingIdentity();
+                        try {
+                            mUpdatesStopped = true;
+                            processValuesLocked(false);
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
+                    }
+                } catch (NumberFormatException ex) {
+                    pw.println("Bad value: " + value);
+                    return -1;
+                }
+            } break;
+            case "reset": {
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, null);
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    if (mUpdatesStopped) {
+                        mUpdatesStopped = false;
+                        mBatteryProps.set(mLastBatteryProps);
+                        processValuesLocked(false);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } break;
+            default:
+                return shell.handleDefaultCommands(cmd);
+        }
+        return 0;
+    }
+
+    private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mLock) {
             if (args == null || args.length == 0 || "-a".equals(args[0])) {
                 pw.println("Current Battery Service state:");
@@ -635,91 +774,13 @@
                 pw.println("  voltage: " + mBatteryProps.batteryVoltage);
                 pw.println("  temperature: " + mBatteryProps.batteryTemperature);
                 pw.println("  technology: " + mBatteryProps.batteryTechnology);
-
-            } else if ("unplug".equals(args[0])) {
-                if (!mUpdatesStopped) {
-                    mLastBatteryProps.set(mBatteryProps);
-                }
-                mBatteryProps.chargerAcOnline = false;
-                mBatteryProps.chargerUsbOnline = false;
-                mBatteryProps.chargerWirelessOnline = false;
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    mUpdatesStopped = true;
-                    processValuesLocked(false);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-
-            } else if (args.length == 3 && "set".equals(args[0])) {
-                String key = args[1];
-                String value = args[2];
-                try {
-                    if (!mUpdatesStopped) {
-                        mLastBatteryProps.set(mBatteryProps);
-                    }
-                    boolean update = true;
-                    if ("ac".equals(key)) {
-                        mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
-                    } else if ("usb".equals(key)) {
-                        mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
-                    } else if ("wireless".equals(key)) {
-                        mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
-                    } else if ("status".equals(key)) {
-                        mBatteryProps.batteryStatus = Integer.parseInt(value);
-                    } else if ("level".equals(key)) {
-                        mBatteryProps.batteryLevel = Integer.parseInt(value);
-                    } else if ("invalid".equals(key)) {
-                        mInvalidCharger = Integer.parseInt(value);
-                    } else {
-                        pw.println("Unknown set option: " + key);
-                        update = false;
-                    }
-                    if (update) {
-                        long ident = Binder.clearCallingIdentity();
-                        try {
-                            mUpdatesStopped = true;
-                            processValuesLocked(false);
-                        } finally {
-                            Binder.restoreCallingIdentity(ident);
-                        }
-                    }
-                } catch (NumberFormatException ex) {
-                    pw.println("Bad value: " + value);
-                }
-
-            } else if (args.length == 1 && "reset".equals(args[0])) {
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    if (mUpdatesStopped) {
-                        mUpdatesStopped = false;
-                        mBatteryProps.set(mLastBatteryProps);
-                        processValuesLocked(false);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
             } else {
-                pw.println("Dump current battery state, or:");
-                pw.println("  set [ac|usb|wireless|status|level|invalid] <value>");
-                pw.println("  unplug");
-                pw.println("  reset");
+                Shell shell = new Shell();
+                shell.exec(mBinderService, null, fd, null, args, new ResultReceiver(null));
             }
         }
     }
 
-    private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
-        @Override
-        public void onUEvent(UEventObserver.UEvent event) {
-            final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
-            synchronized (mLock) {
-                if (mInvalidCharger != invalidCharger) {
-                    mInvalidCharger = invalidCharger;
-                }
-            }
-        }
-    };
-
     private final class Led {
         private final Light mBatteryLight;
 
@@ -776,8 +837,7 @@
     }
 
     private final class BatteryListener extends IBatteryPropertiesListener.Stub {
-        @Override
-        public void batteryPropertiesChanged(BatteryProperties props) {
+        @Override public void batteryPropertiesChanged(BatteryProperties props) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 BatteryService.this.update(props);
@@ -788,8 +848,7 @@
     }
 
     private final class BinderService extends Binder {
-        @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
 
@@ -799,7 +858,12 @@
                 return;
             }
 
-            dumpInternal(pw, args);
+            dumpInternal(fd, pw, args);
+        }
+
+        @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
+                FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+            (new Shell()).exec(this, in, out, err, args, resultReceiver);
         }
     }