Move native netd calls to varargs.

Uses argument escaping inside NativeDaemonConnector, using varargs
to separate boundaries.  Also introduces Command object to help build
argument lists.

Bug: 5472606
Change-Id: I357979fc19bb0171a056e690064e01b5a7119501
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 1e98f93..cc2bcd9 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -29,6 +29,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charsets;
 import java.util.ArrayList;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -122,13 +123,15 @@
 
                 for (int i = 0; i < count; i++) {
                     if (buffer[i] == 0) {
-                        final String rawEvent = new String(buffer, start, i - start);
+                        final String rawEvent = new String(
+                                buffer, start, i - start, Charsets.UTF_8);
                         if (LOGD) Slog.d(TAG, "RCV <- " + rawEvent);
 
                         try {
                             final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
                                     rawEvent);
                             if (event.isClassUnsolicited()) {
+                                // TODO: migrate to sending NativeDaemonEvent instances
                                 mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
                                         event.getCode(), event.getRawEvent()));
                             } else {
@@ -213,7 +216,7 @@
             throw new NativeDaemonConnectorException("missing output stream");
         } else {
             try {
-                mOutputStream.write(builder.toString().getBytes());
+                mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
             } catch (IOException e) {
                 throw new NativeDaemonConnectorException("problem sending command", e);
             }
@@ -223,9 +226,62 @@
     }
 
     /**
-     * Issue a command to the native daemon and return the responses.
+     * Issue the given command to the native daemon and return a single expected
+     * response.
+     *
+     * @throws NativeDaemonConnectorException when problem communicating with
+     *             native daemon, or if the response matches
+     *             {@link NativeDaemonEvent#isClassClientError()} or
+     *             {@link NativeDaemonEvent#isClassServerError()}.
      */
-    public NativeDaemonEvent[] execute(String cmd, Object... args)
+    public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
+        return execute(cmd.mCmd, cmd.mArguments.toArray());
+    }
+
+    /**
+     * Issue the given command to the native daemon and return a single expected
+     * response.
+     *
+     * @throws NativeDaemonConnectorException when problem communicating with
+     *             native daemon, or if the response matches
+     *             {@link NativeDaemonEvent#isClassClientError()} or
+     *             {@link NativeDaemonEvent#isClassServerError()}.
+     */
+    public NativeDaemonEvent execute(String cmd, Object... args)
+            throws NativeDaemonConnectorException {
+        final NativeDaemonEvent[] events = executeForList(cmd, args);
+        if (events.length != 1) {
+            throw new NativeDaemonConnectorException(
+                    "Expected exactly one response, but received " + events.length);
+        }
+        return events[0];
+    }
+
+    /**
+     * Issue the given command to the native daemon and return any
+     * {@link NativeDaemonEvent#isClassContinue()} responses, including the
+     * final terminal response.
+     *
+     * @throws NativeDaemonConnectorException when problem communicating with
+     *             native daemon, or if the response matches
+     *             {@link NativeDaemonEvent#isClassClientError()} or
+     *             {@link NativeDaemonEvent#isClassServerError()}.
+     */
+    public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
+        return executeForList(cmd.mCmd, cmd.mArguments.toArray());
+    }
+
+    /**
+     * Issue the given command to the native daemon and return any
+     * {@link NativeDaemonEvent#isClassContinue()} responses, including the
+     * final terminal response.
+     *
+     * @throws NativeDaemonConnectorException when problem communicating with
+     *             native daemon, or if the response matches
+     *             {@link NativeDaemonEvent#isClassClientError()} or
+     *             {@link NativeDaemonEvent#isClassServerError()}.
+     */
+    public NativeDaemonEvent[] executeForList(String cmd, Object... args)
             throws NativeDaemonConnectorException {
         synchronized (mDaemonLock) {
             return executeLocked(cmd, args);
@@ -270,7 +326,7 @@
     @Deprecated
     public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
         final ArrayList<String> rawEvents = Lists.newArrayList();
-        final NativeDaemonEvent[] events = execute(cmd);
+        final NativeDaemonEvent[] events = executeForList(cmd);
         for (NativeDaemonEvent event : events) {
             rawEvents.add(event.getRawEvent());
         }
@@ -281,11 +337,12 @@
      * Issues a list command and returns the cooked list of all
      * {@link NativeDaemonEvent#getMessage()} which match requested code.
      */
+    @Deprecated
     public String[] doListCommand(String cmd, int expectedCode)
             throws NativeDaemonConnectorException {
         final ArrayList<String> list = Lists.newArrayList();
 
-        final NativeDaemonEvent[] events = execute(cmd);
+        final NativeDaemonEvent[] events = executeForList(cmd);
         for (int i = 0; i < events.length - 1; i++) {
             final NativeDaemonEvent event = events[i];
             final int code = event.getCode();
@@ -351,6 +408,26 @@
         }
     }
 
+    /**
+     * Command builder that handles argument list building.
+     */
+    public static class Command {
+        private String mCmd;
+        private ArrayList<Object> mArguments = Lists.newArrayList();
+
+        public Command(String cmd, Object... args) {
+            mCmd = cmd;
+            for (Object arg : args) {
+                appendArg(arg);
+            }
+        }
+
+        public Command appendArg(Object arg) {
+            mArguments.add(arg);
+            return this;
+        }
+    }
+
     /** {@inheritDoc} */
     public void monitor() {
         synchronized (mDaemonLock) { }
diff --git a/services/java/com/android/server/NativeDaemonEvent.java b/services/java/com/android/server/NativeDaemonEvent.java
index b1d0788..62084c0 100644
--- a/services/java/com/android/server/NativeDaemonEvent.java
+++ b/services/java/com/android/server/NativeDaemonEvent.java
@@ -16,6 +16,10 @@
 
 package com.android.server;
 
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
 /**
  * Parsed event from native side of {@link NativeDaemonConnector}.
  */
@@ -89,6 +93,17 @@
     }
 
     /**
+     * Verify this event matches the given code.
+     *
+     * @throws IllegalStateException if {@link #getCode()} doesn't match.
+     */
+    public void checkCode(int code) {
+        if (mCode != code) {
+            throw new IllegalStateException("Expected " + code + " but was: " + this);
+        }
+    }
+
+    /**
      * Parse the given raw event into {@link NativeDaemonEvent} instance.
      *
      * @throws IllegalArgumentException when line doesn't match format expected
@@ -110,4 +125,18 @@
         final String message = rawEvent.substring(splitIndex + 1);
         return new NativeDaemonEvent(code, message, rawEvent);
     }
+
+    /**
+     * Filter the given {@link NativeDaemonEvent} list, returning
+     * {@link #getMessage()} for any events matching the requested code.
+     */
+    public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
+        final ArrayList<String> result = Lists.newArrayList();
+        for (NativeDaemonEvent event : events) {
+            if (event.getCode() == matchCode) {
+                result.add(event.getMessage());
+            }
+        }
+        return result.toArray(new String[result.size()]);
+    }
 }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 0da60b7..f7bf8b5 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -27,6 +27,16 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Secure.NETSTATS_ENABLED;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceRxThrottleResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceTxThrottleResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
 import android.content.Context;
@@ -48,6 +58,7 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.server.NativeDaemonConnector.Command;
 import com.google.android.collect.Sets;
 
 import java.io.BufferedReader;
@@ -79,8 +90,8 @@
     private static final boolean DBG = false;
     private static final String NETD_TAG = "NetdConnector";
 
-    private static final int ADD = 1;
-    private static final int REMOVE = 2;
+    private static final String ADD = "add";
+    private static final String REMOVE = "remove";
 
     private static final String DEFAULT = "default";
     private static final String SECONDARY = "secondary";
@@ -182,7 +193,7 @@
         if (hasKernelSupport && shouldEnable) {
             Slog.d(TAG, "enabling bandwidth control");
             try {
-                mConnector.doCommand("bandwidth enable");
+                mConnector.execute("bandwidth", "enable");
                 mBandwidthControlEnabled = true;
             } catch (NativeDaemonConnectorException e) {
                 Log.wtf(TAG, "problem enabling bandwidth controls", e);
@@ -281,7 +292,6 @@
      * Let us know the daemon is connected
      */
     protected void onDaemonConnected() {
-        if (DBG) Slog.d(TAG, "onConnected");
         mConnectedSignal.countDown();
     }
 
@@ -358,7 +368,8 @@
     public String[] listInterfaces() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
         try {
-            return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+            return NativeDaemonEvent.filterMessageList(
+                    mConnector.executeForList("interface", "list"), InterfaceListResult);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -367,43 +378,33 @@
     @Override
     public InterfaceConfiguration getInterfaceConfig(String iface) {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
-        String rsp;
+
+        final NativeDaemonEvent event;
         try {
-            rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+            event = mConnector.execute("interface", "getcfg", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
-        Slog.d(TAG, String.format("rsp <%s>", rsp));
 
-        // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3]
-        StringTokenizer st = new StringTokenizer(rsp);
+        event.checkCode(InterfaceGetCfgResult);
+
+        // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3
+        final StringTokenizer st = new StringTokenizer(event.getMessage());
 
         InterfaceConfiguration cfg;
         try {
-            try {
-                int code = Integer.parseInt(st.nextToken(" "));
-                if (code != NetdResponseCode.InterfaceGetCfgResult) {
-                    throw new IllegalStateException(
-                        String.format("Expected code %d, but got %d",
-                                NetdResponseCode.InterfaceGetCfgResult, code));
-                }
-            } catch (NumberFormatException nfe) {
-                throw new IllegalStateException(
-                        String.format("Invalid response from daemon (%s)", rsp));
-            }
-
             cfg = new InterfaceConfiguration();
             cfg.setHardwareAddress(st.nextToken(" "));
             InetAddress addr = null;
             int prefixLength = 0;
             try {
-                addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
+                addr = NetworkUtils.numericToInetAddress(st.nextToken());
             } catch (IllegalArgumentException iae) {
                 Slog.e(TAG, "Failed to parse ipaddr", iae);
             }
 
             try {
-                prefixLength = Integer.parseInt(st.nextToken(" "));
+                prefixLength = Integer.parseInt(st.nextToken());
             } catch (NumberFormatException nfe) {
                 Slog.e(TAG, "Failed to parse prefixLength", nfe);
             }
@@ -413,10 +414,8 @@
                 cfg.setFlag(st.nextToken());
             }
         } catch (NoSuchElementException nsee) {
-            throw new IllegalStateException(
-                    String.format("Invalid response from daemon (%s)", rsp));
+            throw new IllegalStateException("Invalid response from daemon: " + event);
         }
-        Slog.d(TAG, String.format("flags <%s>", cfg.getFlags()));
         return cfg;
     }
 
@@ -427,12 +426,16 @@
         if (linkAddr == null || linkAddr.getAddress() == null) {
             throw new IllegalStateException("Null LinkAddress given");
         }
-        String cmd = String.format("interface setcfg %s %s %d %s", iface,
+
+        final Command cmd = new Command("interface", "setcfg", iface,
                 linkAddr.getAddress().getHostAddress(),
-                linkAddr.getNetworkPrefixLength(),
-                cfg.getFlags());
+                linkAddr.getNetworkPrefixLength());
+        for (String flag : cfg.getFlags()) {
+            cmd.appendArg(flag);
+        }
+
         try {
-            mConnector.doCommand(cmd);
+            mConnector.execute(cmd);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -457,10 +460,9 @@
     @Override
     public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
-        String cmd = String.format("interface ipv6privacyextensions %s %s", iface,
-                enable ? "enable" : "disable");
         try {
-            mConnector.doCommand(cmd);
+            mConnector.execute(
+                    "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -471,9 +473,8 @@
     @Override
     public void clearInterfaceAddresses(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
-        String cmd = String.format("interface clearaddrs %s", iface);
         try {
-            mConnector.doCommand(cmd);
+            mConnector.execute("interface", "clearaddrs", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -483,7 +484,7 @@
     public void enableIpv6(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand(String.format("interface ipv6 %s enable", iface));
+            mConnector.execute("interface", "ipv6", iface, "enable");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -493,7 +494,7 @@
     public void disableIpv6(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand(String.format("interface ipv6 %s disable", iface));
+            mConnector.execute("interface", "ipv6", iface, "disable");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -523,52 +524,28 @@
         modifyRoute(interfaceName, REMOVE, route, SECONDARY);
     }
 
-    private void modifyRoute(String interfaceName, int action, RouteInfo route, String type) {
-        ArrayList<String> rsp;
-
-        StringBuilder cmd;
-
-        switch (action) {
-            case ADD:
-            {
-                cmd = new StringBuilder("interface route add " + interfaceName + " " + type);
-                break;
-            }
-            case REMOVE:
-            {
-                cmd = new StringBuilder("interface route remove " + interfaceName + " " + type);
-                break;
-            }
-            default:
-                throw new IllegalStateException("Unknown action type " + action);
-        }
+    private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) {
+        final Command cmd = new Command("interface", "route", action, interfaceName, type);
 
         // create triplet: dest-ip-addr prefixlength gateway-ip-addr
-        LinkAddress la = route.getDestination();
-        cmd.append(' ');
-        cmd.append(la.getAddress().getHostAddress());
-        cmd.append(' ');
-        cmd.append(la.getNetworkPrefixLength());
-        cmd.append(' ');
+        final LinkAddress la = route.getDestination();
+        cmd.appendArg(la.getAddress().getHostAddress());
+        cmd.appendArg(la.getNetworkPrefixLength());
+
         if (route.getGateway() == null) {
             if (la.getAddress() instanceof Inet4Address) {
-                cmd.append("0.0.0.0");
+                cmd.appendArg("0.0.0.0");
             } else {
-                cmd.append ("::0");
+                cmd.appendArg("::0");
             }
         } else {
-            cmd.append(route.getGateway().getHostAddress());
-        }
-        try {
-            rsp = mConnector.doCommand(cmd.toString());
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            cmd.appendArg(route.getGateway().getHostAddress());
         }
 
-        if (DBG) {
-            for (String line : rsp) {
-                Log.v(TAG, "add route response is " + line);
-            }
+        try {
+            mConnector.execute(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
         }
     }
 
@@ -672,7 +649,7 @@
                 }
             }
         }
-        return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
+        return routes.toArray(new RouteInfo[routes.size()]);
     }
 
     @Override
@@ -687,36 +664,23 @@
     public boolean getIpForwardingEnabled() throws IllegalStateException{
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
 
-        ArrayList<String> rsp;
+        final NativeDaemonEvent event;
         try {
-            rsp = mConnector.doCommand("ipfwd status");
+            event = mConnector.execute("ipfwd", "status");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
 
-        for (String line : rsp) {
-            String[] tok = line.split(" ");
-            if (tok.length < 3) {
-                Slog.e(TAG, "Malformed response from native daemon: " + line);
-                return false;
-            }
-
-            int code = Integer.parseInt(tok[0]);
-            if (code == NetdResponseCode.IpFwdStatusResult) {
-                // 211 Forwarding <enabled/disabled>
-                return "enabled".equals(tok[2]);
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
+        // 211 Forwarding enabled
+        event.checkCode(IpFwdStatusResult);
+        return event.getMessage().endsWith("enabled");
     }
 
     @Override
     public void setIpForwardingEnabled(boolean enable) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
+            mConnector.execute("ipfwd", enable ? "enable" : "disable");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -727,13 +691,14 @@
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         // cmd is "tether start first_start first_stop second_start second_stop ..."
         // an odd number of addrs will fail
-        String cmd = "tether start";
+
+        final Command cmd = new Command("tether", "start");
         for (String d : dhcpRange) {
-            cmd += " " + d;
+            cmd.appendArg(d);
         }
 
         try {
-            mConnector.doCommand(cmd);
+            mConnector.execute(cmd);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -743,7 +708,7 @@
     public void stopTethering() {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand("tether stop");
+            mConnector.execute("tether", "stop");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -753,34 +718,23 @@
     public boolean isTetheringStarted() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
 
-        ArrayList<String> rsp;
+        final NativeDaemonEvent event;
         try {
-            rsp = mConnector.doCommand("tether status");
+            event = mConnector.execute("tether", "status");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
 
-        for (String line : rsp) {
-            String[] tok = line.split(" ");
-            if (tok.length < 3) {
-                throw new IllegalStateException("Malformed response for tether status: " + line);
-            }
-            int code = Integer.parseInt(tok[0]);
-            if (code == NetdResponseCode.TetherStatusResult) {
-                // XXX: Tethering services <started/stopped> <TBD>...
-                return "started".equals(tok[2]);
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
+        // 210 Tethering services started
+        event.checkCode(TetherStatusResult);
+        return event.getMessage().endsWith("started");
     }
 
     @Override
     public void tetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand("tether interface add " + iface);
+            mConnector.execute("tether", "interface", "add", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -790,7 +744,7 @@
     public void untetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand("tether interface remove " + iface);
+            mConnector.execute("tether", "interface", "remove", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -800,8 +754,9 @@
     public String[] listTetheredInterfaces() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
         try {
-            return mConnector.doListCommand(
-                    "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+            return NativeDaemonEvent.filterMessageList(
+                    mConnector.executeForList("tether", "interface", "list"),
+                    TetherInterfaceListResult);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -810,18 +765,16 @@
     @Override
     public void setDnsForwarders(String[] dns) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+
+        final Command cmd = new Command("tether", "dns", "set");
+        for (String s : dns) {
+            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
+        }
+
         try {
-            String cmd = "tether dns set";
-            for (String s : dns) {
-                cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
-            }
-            try {
-                mConnector.doCommand(cmd);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
-            }
-        } catch (IllegalArgumentException e) {
-            throw new IllegalStateException("Error resolving dns name", e);
+            mConnector.execute(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
         }
     }
 
@@ -829,34 +782,34 @@
     public String[] getDnsForwarders() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
         try {
-            return mConnector.doListCommand(
-                    "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+            return NativeDaemonEvent.filterMessageList(
+                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
     }
 
-    private void modifyNat(String cmd, String internalInterface, String externalInterface)
+    private void modifyNat(String action, String internalInterface, String externalInterface)
             throws SocketException {
-        cmd = String.format("nat %s %s %s", cmd, internalInterface, externalInterface);
+        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
 
-        NetworkInterface internalNetworkInterface =
-                NetworkInterface.getByName(internalInterface);
+        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
+                internalInterface);
         if (internalNetworkInterface == null) {
-            cmd += " 0";
+            cmd.appendArg("0");
         } else {
-            Collection<InterfaceAddress>interfaceAddresses =
-                    internalNetworkInterface.getInterfaceAddresses();
-            cmd += " " + interfaceAddresses.size();
+            Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
+                    .getInterfaceAddresses();
+            cmd.appendArg(interfaceAddresses.size());
             for (InterfaceAddress ia : interfaceAddresses) {
-                InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(),
-                        ia.getNetworkPrefixLength());
-                cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength();
+                InetAddress addr = NetworkUtils.getNetworkPart(
+                        ia.getAddress(), ia.getNetworkPrefixLength());
+                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
             }
         }
 
         try {
-            mConnector.doCommand(cmd);
+            mConnector.execute(cmd);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -865,26 +818,20 @@
     @Override
     public void enableNat(String internalInterface, String externalInterface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
-        if (DBG) Log.d(TAG, "enableNat(" + internalInterface + ", " + externalInterface + ")");
         try {
             modifyNat("enable", internalInterface, externalInterface);
-        } catch (Exception e) {
-            Log.e(TAG, "enableNat got Exception " + e.toString());
-            throw new IllegalStateException(
-                    "Unable to communicate to native daemon for enabling NAT interface");
+        } catch (SocketException e) {
+            throw new IllegalStateException(e);
         }
     }
 
     @Override
     public void disableNat(String internalInterface, String externalInterface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
-        if (DBG) Log.d(TAG, "disableNat(" + internalInterface + ", " + externalInterface + ")");
         try {
             modifyNat("disable", internalInterface, externalInterface);
-        } catch (Exception e) {
-            Log.e(TAG, "disableNat got Exception " + e.toString());
-            throw new IllegalStateException(
-                    "Unable to communicate to native daemon for disabling NAT interface");
+        } catch (SocketException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -892,7 +839,8 @@
     public String[] listTtys() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
         try {
-            return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+            return NativeDaemonEvent.filterMessageList(
+                    mConnector.executeForList("list_ttys"), TtyListResult);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -903,13 +851,11 @@
             String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
+            mConnector.execute("pppd", "attach", tty,
                     NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
                     NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
                     NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
-                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
-        } catch (IllegalArgumentException e) {
-            throw new IllegalStateException("Error resolving addr", e);
+                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -919,7 +865,7 @@
     public void detachPppd(String tty) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand(String.format("pppd detach %s", tty));
+            mConnector.execute("pppd", "detach", tty);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -932,42 +878,20 @@
         mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG);
         try {
             wifiFirmwareReload(wlanIface, "AP");
-            mConnector.doCommand(String.format("softap start " + wlanIface));
+            mConnector.execute("softap", "start", wlanIface);
             if (wifiConfig == null) {
-                mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+                mConnector.execute("softap", "set", wlanIface, softapIface);
             } else {
-                /**
-                 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
-                 * argv1 - wlan interface
-                 * argv2 - softap interface
-                 * argv3 - SSID
-                 * argv4 - Security
-                 * argv5 - Key
-                 * argv6 - Channel
-                 * argv7 - Preamble
-                 * argv8 - Max SCB
-                 */
-                 String str = String.format("softap set " + wlanIface + " " + softapIface +
-                                       " %s %s %s", convertQuotedString(wifiConfig.SSID),
-                                       getSecurityType(wifiConfig),
-                                       convertQuotedString(wifiConfig.preSharedKey));
-                mConnector.doCommand(str);
+                mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
+                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);
             }
-            mConnector.doCommand(String.format("softap startap"));
+            mConnector.execute("softap", "startap");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
     }
 
-    private String convertQuotedString(String s) {
-        if (s == null) {
-            return s;
-        }
-        /* Replace \ with \\, then " with \" and add quotes at end */
-        return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
-    }
-
-    private String getSecurityType(WifiConfiguration wifiConfig) {
+    private static String getSecurityType(WifiConfiguration wifiConfig) {
         switch (wifiConfig.getAuthType()) {
             case KeyMgmt.WPA_PSK:
                 return "wpa-psk";
@@ -984,7 +908,7 @@
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG);
         try {
-            mConnector.doCommand(String.format("softap fwreload " + wlanIface + " " + mode));
+            mConnector.execute("softap", "fwreload", wlanIface, mode);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -995,8 +919,8 @@
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG);
         try {
-            mConnector.doCommand("softap stopap");
-            mConnector.doCommand("softap stop " + wlanIface);
+            mConnector.execute("softap", "stopap");
+            mConnector.execute("softap", "stop", wlanIface);
             wifiFirmwareReload(wlanIface, "STA");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
@@ -1009,58 +933,16 @@
         mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG);
         try {
             if (wifiConfig == null) {
-                mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+                mConnector.execute("softap", "set", wlanIface, softapIface);
             } else {
-                String str = String.format("softap set " + wlanIface + " " + softapIface
-                        + " %s %s %s", convertQuotedString(wifiConfig.SSID),
-                        getSecurityType(wifiConfig),
-                        convertQuotedString(wifiConfig.preSharedKey));
-                mConnector.doCommand(str);
+                mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
+                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);
             }
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
     }
 
-    private long getInterfaceCounter(String iface, boolean rx) {
-        mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
-        try {
-            String rsp;
-            try {
-                rsp = mConnector.doCommand(
-                        String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
-            } catch (NativeDaemonConnectorException e1) {
-                Slog.e(TAG, "Error communicating with native daemon", e1);
-                return -1;
-            }
-
-            String[] tok = rsp.split(" ");
-            if (tok.length < 2) {
-                Slog.e(TAG, String.format("Malformed response for reading %s interface",
-                        (rx ? "rx" : "tx")));
-                return -1;
-            }
-
-            int code;
-            try {
-                code = Integer.parseInt(tok[0]);
-            } catch (NumberFormatException nfe) {
-                Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
-                return -1;
-            }
-            if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
-                    !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
-                Slog.e(TAG, String.format("Unexpected response code %d", code));
-                return -1;
-            }
-            return Long.parseLong(tok[1]);
-        } catch (Exception e) {
-            Slog.e(TAG, String.format(
-                    "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
-        }
-        return -1;
-    }
-
     @Override
     public NetworkStats getNetworkStatsSummary() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
@@ -1086,12 +968,9 @@
                 throw new IllegalStateException("iface " + iface + " already has quota");
             }
 
-            final StringBuilder command = new StringBuilder();
-            command.append("bandwidth setiquota ").append(iface).append(" ").append(quotaBytes);
-
             try {
                 // TODO: support quota shared across interfaces
-                mConnector.doCommand(command.toString());
+                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
                 mActiveQuotaIfaces.add(iface);
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
@@ -1113,15 +992,12 @@
                 return;
             }
 
-            final StringBuilder command = new StringBuilder();
-            command.append("bandwidth removeiquota ").append(iface);
-
             mActiveQuotaIfaces.remove(iface);
             mActiveAlertIfaces.remove(iface);
 
             try {
                 // TODO: support quota shared across interfaces
-                mConnector.doCommand(command.toString());
+                mConnector.execute("bandwidth", "removeiquota", iface);
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
             }
@@ -1146,13 +1022,9 @@
                 throw new IllegalStateException("iface " + iface + " already has alert");
             }
 
-            final StringBuilder command = new StringBuilder();
-            command.append("bandwidth setinterfacealert ").append(iface).append(" ").append(
-                    alertBytes);
-
             try {
                 // TODO: support alert shared across interfaces
-                mConnector.doCommand(command.toString());
+                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
                 mActiveAlertIfaces.add(iface);
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
@@ -1174,12 +1046,9 @@
                 return;
             }
 
-            final StringBuilder command = new StringBuilder();
-            command.append("bandwidth removeinterfacealert ").append(iface);
-
             try {
                 // TODO: support alert shared across interfaces
-                mConnector.doCommand(command.toString());
+                mConnector.execute("bandwidth", "removeinterfacealert", iface);
                 mActiveAlertIfaces.remove(iface);
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
@@ -1195,11 +1064,8 @@
         // TODO: eventually migrate to be always enabled
         if (!mBandwidthControlEnabled) return;
 
-        final StringBuilder command = new StringBuilder();
-        command.append("bandwidth setglobalalert ").append(alertBytes);
-
         try {
-            mConnector.doCommand(command.toString());
+            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1220,17 +1086,9 @@
                 return;
             }
 
-            final StringBuilder command = new StringBuilder();
-            command.append("bandwidth");
-            if (rejectOnQuotaInterfaces) {
-                command.append(" addnaughtyapps");
-            } else {
-                command.append(" removenaughtyapps");
-            }
-            command.append(" ").append(uid);
-
             try {
-                mConnector.doCommand(command.toString());
+                mConnector.execute("bandwidth",
+                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
                 if (rejectOnQuotaInterfaces) {
                     mUidRejectOnQuota.put(uid, true);
                 } else {
@@ -1277,33 +1135,19 @@
     }
 
     private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
-        final StringBuilder command = new StringBuilder();
-        command.append("bandwidth gettetherstats ").append(ifaceIn).append(" ").append(ifaceOut);
-
-        final String rsp;
+        final NativeDaemonEvent event;
         try {
-            rsp = mConnector.doCommand(command.toString()).get(0);
+            event = mConnector.execute("bandwidth", "gettetherstats", ifaceIn, ifaceOut);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
 
-        final String[] tok = rsp.split(" ");
-        /* Expecting: "code ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets" */
-        if (tok.length != 7) {
-            throw new IllegalStateException("Native daemon returned unexpected result: " + rsp);
-        }
+        event.checkCode(TetheringStatsResult);
 
-        final int code;
-        try {
-            code = Integer.parseInt(tok[0]);
-        } catch (NumberFormatException e) {
-            throw new IllegalStateException(
-                    "Failed to parse native daemon return code for " + ifaceIn + " " + ifaceOut);
-        }
-        if (code != NetdResponseCode.TetheringStatsResult) {
-            throw new IllegalStateException(
-                    "Unexpected return code from native daemon for " + ifaceIn + " " + ifaceOut);
-        }
+        // 221 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
+        final StringTokenizer tok = new StringTokenizer(event.getMessage());
+        tok.nextToken();
+        tok.nextToken();
 
         try {
             final NetworkStats.Entry entry = new NetworkStats.Entry();
@@ -1311,10 +1155,10 @@
             entry.uid = UID_TETHERING;
             entry.set = SET_DEFAULT;
             entry.tag = TAG_NONE;
-            entry.rxBytes = Long.parseLong(tok[3]);
-            entry.rxPackets = Long.parseLong(tok[4]);
-            entry.txBytes = Long.parseLong(tok[5]);
-            entry.txPackets = Long.parseLong(tok[6]);
+            entry.rxBytes = Long.parseLong(tok.nextToken());
+            entry.rxPackets = Long.parseLong(tok.nextToken());
+            entry.txBytes = Long.parseLong(tok.nextToken());
+            entry.txPackets = Long.parseLong(tok.nextToken());
             return entry;
         } catch (NumberFormatException e) {
             throw new IllegalStateException(
@@ -1326,8 +1170,7 @@
     public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            mConnector.doCommand(String.format(
-                    "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+            mConnector.execute("interface", "setthrottle", iface, rxKbps, txKbps);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1335,40 +1178,25 @@
 
     private int getInterfaceThrottle(String iface, boolean rx) {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
+
+        final NativeDaemonEvent event;
         try {
-            String rsp;
-            try {
-                rsp = mConnector.doCommand(
-                        String.format("interface getthrottle %s %s", iface,
-                                (rx ? "rx" : "tx"))).get(0);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
-            }
-
-            String[] tok = rsp.split(" ");
-            if (tok.length < 2) {
-                Slog.e(TAG, "Malformed response to getthrottle command");
-                return -1;
-            }
-
-            int code;
-            try {
-                code = Integer.parseInt(tok[0]);
-            } catch (NumberFormatException nfe) {
-                Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
-                return -1;
-            }
-            if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
-                    !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
-                Slog.e(TAG, String.format("Unexpected response code %d", code));
-                return -1;
-            }
-            return Integer.parseInt(tok[1]);
-        } catch (Exception e) {
-            Slog.e(TAG, String.format(
-                    "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
+            event = mConnector.execute("interface", "getthrottle", iface, rx ? "rx" : "tx");
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
         }
-        return -1;
+
+        if (rx) {
+            event.checkCode(InterfaceRxThrottleResult);
+        } else {
+            event.checkCode(InterfaceTxThrottleResult);
+        }
+
+        try {
+            return Integer.parseInt(event.getMessage());
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException("unexpected response:" + event);
+        }
     }
 
     @Override
@@ -1385,9 +1213,7 @@
     public void setDefaultInterfaceForDns(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            String cmd = "resolver setdefaultif " + iface;
-
-            mConnector.doCommand(cmd);
+            mConnector.execute("resolver", "setdefaultif", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1396,17 +1222,17 @@
     @Override
     public void setDnsServersForInterface(String iface, String[] servers) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
-        try {
-            String cmd = "resolver setifdns " + iface;
-            for (String s : servers) {
-                InetAddress a = NetworkUtils.numericToInetAddress(s);
-                if (a.isAnyLocalAddress() == false) {
-                    cmd += " " + a.getHostAddress();
-                }
+
+        final Command cmd = new Command("resolver", "setifdns", iface);
+        for (String s : servers) {
+            InetAddress a = NetworkUtils.numericToInetAddress(s);
+            if (a.isAnyLocalAddress() == false) {
+                cmd.appendArg(a.getHostAddress());
             }
-            mConnector.doCommand(cmd);
-        } catch (IllegalArgumentException e) {
-            throw new IllegalStateException("Error setting dnsn for interface", e);
+        }
+
+        try {
+            mConnector.execute(cmd);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1416,9 +1242,7 @@
     public void flushDefaultDnsCache() {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            String cmd = "resolver flushdefaultif";
-
-            mConnector.doCommand(cmd);
+            mConnector.execute("resolver", "flushdefaultif");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1428,9 +1252,7 @@
     public void flushInterfaceDnsCache(String iface) {
         mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
         try {
-            String cmd = "resolver flushif " + iface;
-
-            mConnector.doCommand(cmd);
+            mConnector.execute("resolver", "flushif", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }