Loading services/java/com/android/server/MountService.java +5 −1 Original line number Diff line number Diff line Loading @@ -1831,7 +1831,11 @@ class MountService extends IMountService.Stub // to let the UI to clear itself mHandler.postDelayed(new Runnable() { public void run() { try { mConnector.doCommand(String.format("cryptfs restart")); } catch (NativeDaemonConnectorException e) { Slog.e(TAG, "problem executing in background", e); } } }, 1000); // 1 second } Loading services/java/com/android/server/NativeDaemonConnector.java +159 −132 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.os.Message; import android.os.SystemClock; import android.util.Slog; import com.google.android.collect.Lists; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; Loading @@ -32,49 +34,33 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** * Generic connector class for interfacing with a native * daemon which uses the libsysutils FrameworkListener * protocol. * Generic connector class for interfacing with a native daemon which uses the * {@code libsysutils} FrameworkListener protocol. */ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { private static final boolean LOCAL_LOGD = false; private static final boolean LOGD = false; private final String TAG; private BlockingQueue<String> mResponseQueue; private OutputStream mOutputStream; private String TAG = "NativeDaemonConnector"; private String mSocket; private OutputStream mOutputStream; private final BlockingQueue<NativeDaemonEvent> mResponseQueue; private INativeDaemonConnectorCallbacks mCallbacks; private Handler mCallbackHandler; /** Lock held whenever communicating with native daemon. */ private Object mDaemonLock = new Object(); private final Object mDaemonLock = new Object(); private final int BUFFER_SIZE = 4096; class ResponseCode { public static final int ActionInitiated = 100; public static final int CommandOkay = 200; // The range of 400 -> 599 is reserved for cmd failures public static final int OperationFailed = 400; public static final int CommandSyntaxError = 500; public static final int CommandParameterError = 501; public static final int UnsolicitedInformational = 600; // public static final int FailedRangeStart = 400; public static final int FailedRangeEnd = 599; } NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, int responseQueueSize, String logTag) { NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, int responseQueueSize, String logTag) { mCallbacks = callbacks; if (logTag != null) TAG = logTag; mSocket = socket; mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize); mResponseQueue = new LinkedBlockingQueue<NativeDaemonEvent>(responseQueueSize); TAG = logTag != null ? logTag : "NativeDaemonConnector"; } @Override Loading Loading @@ -136,26 +122,26 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo for (int i = 0; i < count; i++) { if (buffer[i] == 0) { String event = new String(buffer, start, i - start); if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event)); final String rawEvent = new String(buffer, start, i - start); if (LOGD) Slog.d(TAG, "RCV <- " + rawEvent); String[] tokens = event.split(" ", 2); try { int code = Integer.parseInt(tokens[0]); if (code >= ResponseCode.UnsolicitedInformational) { mCallbackHandler.sendMessage( mCallbackHandler.obtainMessage(code, event)); final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent( rawEvent); if (event.isClassUnsolicited()) { mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( event.getCode(), event.getRawEvent())); } else { try { mResponseQueue.put(event); } catch (InterruptedException ex) { Slog.e(TAG, "Failed to put response onto queue", ex); Slog.e(TAG, "Failed to put response onto queue: " + ex); } } } catch (NumberFormatException nfe) { Slog.w(TAG, String.format("Bad msg (%s)", event)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Problem parsing message: " + rawEvent, e); } start = i + 1; } } Loading Loading @@ -195,133 +181,174 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo } } private void sendCommandLocked(String command) throws NativeDaemonConnectorException { sendCommandLocked(command, null); } /** * Sends a command to the daemon with a single argument * Send command to daemon, escaping arguments as needed. * * @param command The command to send to the daemon * @param argument The argument to send with the command (or null) * @return the final command issued. */ private void sendCommandLocked(String command, String argument) private String sendCommandLocked(String cmd, Object... args) throws NativeDaemonConnectorException { if (command != null && command.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected command: " + command); // TODO: eventually enforce that cmd doesn't contain arguments if (cmd.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected command: " + cmd); } if (argument != null && argument.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected argument: " + argument); final StringBuilder builder = new StringBuilder(cmd); for (Object arg : args) { final String argString = String.valueOf(arg); if (argString.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected argument: " + arg); } if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument)); if (mOutputStream == null) { Slog.e(TAG, "No connection to daemon", new IllegalStateException()); throw new NativeDaemonConnectorException("No output stream!"); } else { StringBuilder builder = new StringBuilder(command); if (argument != null) { builder.append(argument); builder.append(' '); appendEscaped(builder, argString); } final String unterminated = builder.toString(); if (LOGD) Slog.d(TAG, "SND -> " + unterminated); builder.append('\0'); if (mOutputStream == null) { throw new NativeDaemonConnectorException("missing output stream"); } else { try { mOutputStream.write(builder.toString().getBytes()); } catch (IOException ex) { Slog.e(TAG, "IOException in sendCommand", ex); } catch (IOException e) { throw new NativeDaemonConnectorException("problem sending command", e); } } return unterminated; } /** * Issue a command to the native daemon and return the responses * Issue a command to the native daemon and return the responses. */ public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException { public NativeDaemonEvent[] execute(String cmd, Object... args) throws NativeDaemonConnectorException { synchronized (mDaemonLock) { return doCommandLocked(cmd); return executeLocked(cmd, args); } } private ArrayList<String> doCommandLocked(String cmd) throws NativeDaemonConnectorException { private NativeDaemonEvent[] executeLocked(String cmd, Object... args) throws NativeDaemonConnectorException { final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); mResponseQueue.clear(); sendCommandLocked(cmd); ArrayList<String> response = new ArrayList<String>(); boolean complete = false; int code = -1; final String sentCommand = sendCommandLocked(cmd, args); while (!complete) { NativeDaemonEvent event = null; do { try { // TODO - this should not block forever String line = mResponseQueue.take(); if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line)); String[] tokens = line.split(" "); try { code = Integer.parseInt(tokens[0]); } catch (NumberFormatException nfe) { throw new NativeDaemonConnectorException( String.format("Invalid response from daemon (%s)", line)); event = mResponseQueue.take(); } catch (InterruptedException e) { Slog.w(TAG, "interrupted waiting for event line"); continue; } events.add(event); } while (event.isClassContinue()); if ((code >= 200) && (code < 600)) { complete = true; if (event.isClassClientError()) { throw new NativeDaemonArgumentException(sentCommand, event); } response.add(line); } catch (InterruptedException ex) { Slog.e(TAG, "Failed to process response", ex); if (event.isClassServerError()) { throw new NativeDaemonFailureException(sentCommand, event); } return events.toArray(new NativeDaemonEvent[events.size()]); } if (code >= ResponseCode.FailedRangeStart && code <= ResponseCode.FailedRangeEnd) { /* * Note: The format of the last response in this case is * "NNN <errmsg>" /** * Issue a command to the native daemon and return the raw responses. * * @deprecated callers should move to {@link #execute(String, Object...)} * which returns parsed {@link NativeDaemonEvent}. */ throw new NativeDaemonConnectorException( code, cmd, response.get(response.size()-1).substring(4)); @Deprecated public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException { final ArrayList<String> rawEvents = Lists.newArrayList(); final NativeDaemonEvent[] events = execute(cmd); for (NativeDaemonEvent event : events) { rawEvents.add(event.getRawEvent()); } return response; return rawEvents; } /** * Issues a list command and returns the cooked list * Issues a list command and returns the cooked list of all * {@link NativeDaemonEvent#getMessage()} which match requested code. */ public String[] doListCommand(String cmd, int expectedResponseCode) public String[] doListCommand(String cmd, int expectedCode) throws NativeDaemonConnectorException { final ArrayList<String> list = Lists.newArrayList(); final NativeDaemonEvent[] events = execute(cmd); for (int i = 0; i < events.length - 1; i++) { final NativeDaemonEvent event = events[i]; final int code = event.getCode(); if (code == expectedCode) { list.add(event.getMessage()); } else { throw new NativeDaemonConnectorException( "unexpected list response " + code + " instead of " + expectedCode); } } final NativeDaemonEvent finalEvent = events[events.length - 1]; if (!finalEvent.isClassOk()) { throw new NativeDaemonConnectorException("unexpected final event: " + finalEvent); } ArrayList<String> rsp = doCommand(cmd); String[] rdata = new String[rsp.size()-1]; int idx = 0; return list.toArray(new String[list.size()]); } for (int i = 0; i < rsp.size(); i++) { String line = rsp.get(i); try { String[] tok = line.split(" "); int code = Integer.parseInt(tok[0]); if (code == expectedResponseCode) { rdata[idx++] = line.substring(tok[0].length() + 1); } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line)); int last = rsp.size() -1; if (i != last) { Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd)); for (int j = i; j <= last ; j++) { Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i))); } } return rdata; /** * Append the given argument to {@link StringBuilder}, escaping as needed, * and surrounding with quotes when it contains spaces. */ // @VisibleForTesting static void appendEscaped(StringBuilder builder, String arg) { final boolean hasSpaces = arg.indexOf(' ') >= 0; if (hasSpaces) { builder.append('"'); } final int length = arg.length(); for (int i = 0; i < length; i++) { final char c = arg.charAt(i); if (c == '"') { builder.append("\\\""); } else if (c == '\\') { builder.append("\\\\"); } else { throw new NativeDaemonConnectorException( String.format("Expected list response %d, but got %d", expectedResponseCode, code)); builder.append(c); } } catch (NumberFormatException nfe) { throw new NativeDaemonConnectorException( String.format("Error reading code '%s'", line)); } if (hasSpaces) { builder.append('"'); } } private static class NativeDaemonArgumentException extends NativeDaemonConnectorException { public NativeDaemonArgumentException(String command, NativeDaemonEvent event) { super(command, event); } @Override public IllegalArgumentException rethrowAsParcelableException() { throw new IllegalArgumentException(getMessage(), this); } } private static class NativeDaemonFailureException extends NativeDaemonConnectorException { public NativeDaemonFailureException(String command, NativeDaemonEvent event) { super(command, event); } throw new NativeDaemonConnectorException("Got an empty response"); } /** {@inheritDoc} */ Loading services/java/com/android/server/NativeDaemonConnectorException.java +23 −13 Original line number Diff line number Diff line Loading @@ -16,33 +16,43 @@ package com.android.server; import android.os.Parcel; /** * An exception that indicates there was an error with a NativeDaemonConnector operation * An exception that indicates there was an error with a * {@link NativeDaemonConnector} operation. */ public class NativeDaemonConnectorException extends RuntimeException { private int mCode = -1; public class NativeDaemonConnectorException extends Exception { private String mCmd; private NativeDaemonEvent mEvent; public NativeDaemonConnectorException() {} public NativeDaemonConnectorException(String detailMessage) { super(detailMessage); } public NativeDaemonConnectorException(String error) { super(error); public NativeDaemonConnectorException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } public NativeDaemonConnectorException(int code, String cmd, String error) { super(String.format("Cmd {%s} failed with code %d : {%s}", cmd, code, error)); mCode = code; public NativeDaemonConnectorException(String cmd, NativeDaemonEvent event) { super("command '" + cmd + "' failed with '" + event + "'"); mCmd = cmd; mEvent = event; } public int getCode() { return mCode; return mEvent.getCode(); } public String getCmd() { return mCmd; } /** * Rethrow as a {@link RuntimeException} subclass that is handled by * {@link Parcel#writeException(Exception)}. */ public IllegalArgumentException rethrowAsParcelableException() { throw new IllegalStateException(getMessage(), this); } } services/java/com/android/server/NativeDaemonEvent.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; /** * Parsed event from native side of {@link NativeDaemonConnector}. */ public class NativeDaemonEvent { // TODO: keep class ranges in sync with ResponseCode.h // TODO: swap client and server error ranges to roughly mirror HTTP spec private final int mCode; private final String mMessage; private final String mRawEvent; private NativeDaemonEvent(int code, String message, String rawEvent) { mCode = code; mMessage = message; mRawEvent = rawEvent; } public int getCode() { return mCode; } public String getMessage() { return mMessage; } @Deprecated public String getRawEvent() { return mRawEvent; } @Override public String toString() { return mRawEvent; } /** * Test if event represents a partial response which is continued in * additional subsequent events. */ public boolean isClassContinue() { return mCode >= 100 && mCode < 200; } /** * Test if event represents a command success. */ public boolean isClassOk() { return mCode >= 200 && mCode < 300; } /** * Test if event represents a remote native daemon error. */ public boolean isClassServerError() { return mCode >= 400 && mCode < 500; } /** * Test if event represents a command syntax or argument error. */ public boolean isClassClientError() { return mCode >= 500 && mCode < 600; } /** * Test if event represents an unsolicited event from native daemon. */ public boolean isClassUnsolicited() { return mCode >= 600 && mCode < 700; } /** * Parse the given raw event into {@link NativeDaemonEvent} instance. * * @throws IllegalArgumentException when line doesn't match format expected * from native side. */ public static NativeDaemonEvent parseRawEvent(String rawEvent) { final int splitIndex = rawEvent.indexOf(' '); if (splitIndex == -1) { throw new IllegalArgumentException("unable to find ' ' separator"); } final int code; try { code = Integer.parseInt(rawEvent.substring(0, splitIndex)); } catch (NumberFormatException e) { throw new IllegalArgumentException("problem parsing code", e); } final String message = rawEvent.substring(splitIndex + 1); return new NativeDaemonEvent(code, message, rawEvent); } } services/java/com/android/server/NetworkManagementService.java +16 −18 Original line number Diff line number Diff line Loading @@ -442,27 +442,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void setInterfaceDown(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { InterfaceConfiguration ifcg = getInterfaceConfig(iface); final InterfaceConfiguration ifcg = getInterfaceConfig(iface); ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); setInterfaceConfig(iface, ifcg); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException( "Unable to communicate with native daemon for interface down - " + e); } } @Override public void setInterfaceUp(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { InterfaceConfiguration ifcg = getInterfaceConfig(iface); final InterfaceConfiguration ifcg = getInterfaceConfig(iface); ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); setInterfaceConfig(iface, ifcg); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException( "Unable to communicate with native daemon for interface up - " + e); } } @Override Loading Loading @@ -733,7 +723,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void setIpForwardingEnabled(boolean enable) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); } catch (NativeDaemonConnectorException e) { e.rethrowAsParcelableException(); } } @Override Loading Loading @@ -875,7 +869,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } try { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { e.rethrowAsParcelableException(); } } @Override Loading Loading
services/java/com/android/server/MountService.java +5 −1 Original line number Diff line number Diff line Loading @@ -1831,7 +1831,11 @@ class MountService extends IMountService.Stub // to let the UI to clear itself mHandler.postDelayed(new Runnable() { public void run() { try { mConnector.doCommand(String.format("cryptfs restart")); } catch (NativeDaemonConnectorException e) { Slog.e(TAG, "problem executing in background", e); } } }, 1000); // 1 second } Loading
services/java/com/android/server/NativeDaemonConnector.java +159 −132 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.os.Message; import android.os.SystemClock; import android.util.Slog; import com.google.android.collect.Lists; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; Loading @@ -32,49 +34,33 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** * Generic connector class for interfacing with a native * daemon which uses the libsysutils FrameworkListener * protocol. * Generic connector class for interfacing with a native daemon which uses the * {@code libsysutils} FrameworkListener protocol. */ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { private static final boolean LOCAL_LOGD = false; private static final boolean LOGD = false; private final String TAG; private BlockingQueue<String> mResponseQueue; private OutputStream mOutputStream; private String TAG = "NativeDaemonConnector"; private String mSocket; private OutputStream mOutputStream; private final BlockingQueue<NativeDaemonEvent> mResponseQueue; private INativeDaemonConnectorCallbacks mCallbacks; private Handler mCallbackHandler; /** Lock held whenever communicating with native daemon. */ private Object mDaemonLock = new Object(); private final Object mDaemonLock = new Object(); private final int BUFFER_SIZE = 4096; class ResponseCode { public static final int ActionInitiated = 100; public static final int CommandOkay = 200; // The range of 400 -> 599 is reserved for cmd failures public static final int OperationFailed = 400; public static final int CommandSyntaxError = 500; public static final int CommandParameterError = 501; public static final int UnsolicitedInformational = 600; // public static final int FailedRangeStart = 400; public static final int FailedRangeEnd = 599; } NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, int responseQueueSize, String logTag) { NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, int responseQueueSize, String logTag) { mCallbacks = callbacks; if (logTag != null) TAG = logTag; mSocket = socket; mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize); mResponseQueue = new LinkedBlockingQueue<NativeDaemonEvent>(responseQueueSize); TAG = logTag != null ? logTag : "NativeDaemonConnector"; } @Override Loading Loading @@ -136,26 +122,26 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo for (int i = 0; i < count; i++) { if (buffer[i] == 0) { String event = new String(buffer, start, i - start); if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event)); final String rawEvent = new String(buffer, start, i - start); if (LOGD) Slog.d(TAG, "RCV <- " + rawEvent); String[] tokens = event.split(" ", 2); try { int code = Integer.parseInt(tokens[0]); if (code >= ResponseCode.UnsolicitedInformational) { mCallbackHandler.sendMessage( mCallbackHandler.obtainMessage(code, event)); final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent( rawEvent); if (event.isClassUnsolicited()) { mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( event.getCode(), event.getRawEvent())); } else { try { mResponseQueue.put(event); } catch (InterruptedException ex) { Slog.e(TAG, "Failed to put response onto queue", ex); Slog.e(TAG, "Failed to put response onto queue: " + ex); } } } catch (NumberFormatException nfe) { Slog.w(TAG, String.format("Bad msg (%s)", event)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Problem parsing message: " + rawEvent, e); } start = i + 1; } } Loading Loading @@ -195,133 +181,174 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo } } private void sendCommandLocked(String command) throws NativeDaemonConnectorException { sendCommandLocked(command, null); } /** * Sends a command to the daemon with a single argument * Send command to daemon, escaping arguments as needed. * * @param command The command to send to the daemon * @param argument The argument to send with the command (or null) * @return the final command issued. */ private void sendCommandLocked(String command, String argument) private String sendCommandLocked(String cmd, Object... args) throws NativeDaemonConnectorException { if (command != null && command.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected command: " + command); // TODO: eventually enforce that cmd doesn't contain arguments if (cmd.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected command: " + cmd); } if (argument != null && argument.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected argument: " + argument); final StringBuilder builder = new StringBuilder(cmd); for (Object arg : args) { final String argString = String.valueOf(arg); if (argString.indexOf('\0') >= 0) { throw new IllegalArgumentException("unexpected argument: " + arg); } if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument)); if (mOutputStream == null) { Slog.e(TAG, "No connection to daemon", new IllegalStateException()); throw new NativeDaemonConnectorException("No output stream!"); } else { StringBuilder builder = new StringBuilder(command); if (argument != null) { builder.append(argument); builder.append(' '); appendEscaped(builder, argString); } final String unterminated = builder.toString(); if (LOGD) Slog.d(TAG, "SND -> " + unterminated); builder.append('\0'); if (mOutputStream == null) { throw new NativeDaemonConnectorException("missing output stream"); } else { try { mOutputStream.write(builder.toString().getBytes()); } catch (IOException ex) { Slog.e(TAG, "IOException in sendCommand", ex); } catch (IOException e) { throw new NativeDaemonConnectorException("problem sending command", e); } } return unterminated; } /** * Issue a command to the native daemon and return the responses * Issue a command to the native daemon and return the responses. */ public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException { public NativeDaemonEvent[] execute(String cmd, Object... args) throws NativeDaemonConnectorException { synchronized (mDaemonLock) { return doCommandLocked(cmd); return executeLocked(cmd, args); } } private ArrayList<String> doCommandLocked(String cmd) throws NativeDaemonConnectorException { private NativeDaemonEvent[] executeLocked(String cmd, Object... args) throws NativeDaemonConnectorException { final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); mResponseQueue.clear(); sendCommandLocked(cmd); ArrayList<String> response = new ArrayList<String>(); boolean complete = false; int code = -1; final String sentCommand = sendCommandLocked(cmd, args); while (!complete) { NativeDaemonEvent event = null; do { try { // TODO - this should not block forever String line = mResponseQueue.take(); if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line)); String[] tokens = line.split(" "); try { code = Integer.parseInt(tokens[0]); } catch (NumberFormatException nfe) { throw new NativeDaemonConnectorException( String.format("Invalid response from daemon (%s)", line)); event = mResponseQueue.take(); } catch (InterruptedException e) { Slog.w(TAG, "interrupted waiting for event line"); continue; } events.add(event); } while (event.isClassContinue()); if ((code >= 200) && (code < 600)) { complete = true; if (event.isClassClientError()) { throw new NativeDaemonArgumentException(sentCommand, event); } response.add(line); } catch (InterruptedException ex) { Slog.e(TAG, "Failed to process response", ex); if (event.isClassServerError()) { throw new NativeDaemonFailureException(sentCommand, event); } return events.toArray(new NativeDaemonEvent[events.size()]); } if (code >= ResponseCode.FailedRangeStart && code <= ResponseCode.FailedRangeEnd) { /* * Note: The format of the last response in this case is * "NNN <errmsg>" /** * Issue a command to the native daemon and return the raw responses. * * @deprecated callers should move to {@link #execute(String, Object...)} * which returns parsed {@link NativeDaemonEvent}. */ throw new NativeDaemonConnectorException( code, cmd, response.get(response.size()-1).substring(4)); @Deprecated public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException { final ArrayList<String> rawEvents = Lists.newArrayList(); final NativeDaemonEvent[] events = execute(cmd); for (NativeDaemonEvent event : events) { rawEvents.add(event.getRawEvent()); } return response; return rawEvents; } /** * Issues a list command and returns the cooked list * Issues a list command and returns the cooked list of all * {@link NativeDaemonEvent#getMessage()} which match requested code. */ public String[] doListCommand(String cmd, int expectedResponseCode) public String[] doListCommand(String cmd, int expectedCode) throws NativeDaemonConnectorException { final ArrayList<String> list = Lists.newArrayList(); final NativeDaemonEvent[] events = execute(cmd); for (int i = 0; i < events.length - 1; i++) { final NativeDaemonEvent event = events[i]; final int code = event.getCode(); if (code == expectedCode) { list.add(event.getMessage()); } else { throw new NativeDaemonConnectorException( "unexpected list response " + code + " instead of " + expectedCode); } } final NativeDaemonEvent finalEvent = events[events.length - 1]; if (!finalEvent.isClassOk()) { throw new NativeDaemonConnectorException("unexpected final event: " + finalEvent); } ArrayList<String> rsp = doCommand(cmd); String[] rdata = new String[rsp.size()-1]; int idx = 0; return list.toArray(new String[list.size()]); } for (int i = 0; i < rsp.size(); i++) { String line = rsp.get(i); try { String[] tok = line.split(" "); int code = Integer.parseInt(tok[0]); if (code == expectedResponseCode) { rdata[idx++] = line.substring(tok[0].length() + 1); } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line)); int last = rsp.size() -1; if (i != last) { Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd)); for (int j = i; j <= last ; j++) { Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i))); } } return rdata; /** * Append the given argument to {@link StringBuilder}, escaping as needed, * and surrounding with quotes when it contains spaces. */ // @VisibleForTesting static void appendEscaped(StringBuilder builder, String arg) { final boolean hasSpaces = arg.indexOf(' ') >= 0; if (hasSpaces) { builder.append('"'); } final int length = arg.length(); for (int i = 0; i < length; i++) { final char c = arg.charAt(i); if (c == '"') { builder.append("\\\""); } else if (c == '\\') { builder.append("\\\\"); } else { throw new NativeDaemonConnectorException( String.format("Expected list response %d, but got %d", expectedResponseCode, code)); builder.append(c); } } catch (NumberFormatException nfe) { throw new NativeDaemonConnectorException( String.format("Error reading code '%s'", line)); } if (hasSpaces) { builder.append('"'); } } private static class NativeDaemonArgumentException extends NativeDaemonConnectorException { public NativeDaemonArgumentException(String command, NativeDaemonEvent event) { super(command, event); } @Override public IllegalArgumentException rethrowAsParcelableException() { throw new IllegalArgumentException(getMessage(), this); } } private static class NativeDaemonFailureException extends NativeDaemonConnectorException { public NativeDaemonFailureException(String command, NativeDaemonEvent event) { super(command, event); } throw new NativeDaemonConnectorException("Got an empty response"); } /** {@inheritDoc} */ Loading
services/java/com/android/server/NativeDaemonConnectorException.java +23 −13 Original line number Diff line number Diff line Loading @@ -16,33 +16,43 @@ package com.android.server; import android.os.Parcel; /** * An exception that indicates there was an error with a NativeDaemonConnector operation * An exception that indicates there was an error with a * {@link NativeDaemonConnector} operation. */ public class NativeDaemonConnectorException extends RuntimeException { private int mCode = -1; public class NativeDaemonConnectorException extends Exception { private String mCmd; private NativeDaemonEvent mEvent; public NativeDaemonConnectorException() {} public NativeDaemonConnectorException(String detailMessage) { super(detailMessage); } public NativeDaemonConnectorException(String error) { super(error); public NativeDaemonConnectorException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } public NativeDaemonConnectorException(int code, String cmd, String error) { super(String.format("Cmd {%s} failed with code %d : {%s}", cmd, code, error)); mCode = code; public NativeDaemonConnectorException(String cmd, NativeDaemonEvent event) { super("command '" + cmd + "' failed with '" + event + "'"); mCmd = cmd; mEvent = event; } public int getCode() { return mCode; return mEvent.getCode(); } public String getCmd() { return mCmd; } /** * Rethrow as a {@link RuntimeException} subclass that is handled by * {@link Parcel#writeException(Exception)}. */ public IllegalArgumentException rethrowAsParcelableException() { throw new IllegalStateException(getMessage(), this); } }
services/java/com/android/server/NativeDaemonEvent.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; /** * Parsed event from native side of {@link NativeDaemonConnector}. */ public class NativeDaemonEvent { // TODO: keep class ranges in sync with ResponseCode.h // TODO: swap client and server error ranges to roughly mirror HTTP spec private final int mCode; private final String mMessage; private final String mRawEvent; private NativeDaemonEvent(int code, String message, String rawEvent) { mCode = code; mMessage = message; mRawEvent = rawEvent; } public int getCode() { return mCode; } public String getMessage() { return mMessage; } @Deprecated public String getRawEvent() { return mRawEvent; } @Override public String toString() { return mRawEvent; } /** * Test if event represents a partial response which is continued in * additional subsequent events. */ public boolean isClassContinue() { return mCode >= 100 && mCode < 200; } /** * Test if event represents a command success. */ public boolean isClassOk() { return mCode >= 200 && mCode < 300; } /** * Test if event represents a remote native daemon error. */ public boolean isClassServerError() { return mCode >= 400 && mCode < 500; } /** * Test if event represents a command syntax or argument error. */ public boolean isClassClientError() { return mCode >= 500 && mCode < 600; } /** * Test if event represents an unsolicited event from native daemon. */ public boolean isClassUnsolicited() { return mCode >= 600 && mCode < 700; } /** * Parse the given raw event into {@link NativeDaemonEvent} instance. * * @throws IllegalArgumentException when line doesn't match format expected * from native side. */ public static NativeDaemonEvent parseRawEvent(String rawEvent) { final int splitIndex = rawEvent.indexOf(' '); if (splitIndex == -1) { throw new IllegalArgumentException("unable to find ' ' separator"); } final int code; try { code = Integer.parseInt(rawEvent.substring(0, splitIndex)); } catch (NumberFormatException e) { throw new IllegalArgumentException("problem parsing code", e); } final String message = rawEvent.substring(splitIndex + 1); return new NativeDaemonEvent(code, message, rawEvent); } }
services/java/com/android/server/NetworkManagementService.java +16 −18 Original line number Diff line number Diff line Loading @@ -442,27 +442,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void setInterfaceDown(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { InterfaceConfiguration ifcg = getInterfaceConfig(iface); final InterfaceConfiguration ifcg = getInterfaceConfig(iface); ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); setInterfaceConfig(iface, ifcg); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException( "Unable to communicate with native daemon for interface down - " + e); } } @Override public void setInterfaceUp(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { InterfaceConfiguration ifcg = getInterfaceConfig(iface); final InterfaceConfiguration ifcg = getInterfaceConfig(iface); ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); setInterfaceConfig(iface, ifcg); } catch (NativeDaemonConnectorException e) { throw new IllegalStateException( "Unable to communicate with native daemon for interface up - " + e); } } @Override Loading Loading @@ -733,7 +723,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void setIpForwardingEnabled(boolean enable) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); } catch (NativeDaemonConnectorException e) { e.rethrowAsParcelableException(); } } @Override Loading Loading @@ -875,7 +869,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } try { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { e.rethrowAsParcelableException(); } } @Override Loading