Merge "Rework Exception Handling for IpSecManager" into pi-dev am: 16c671dc9a

am: 86238ee312

Change-Id: Ic1f560070d12f3bdeb5c07316aad7ebed9719f6f
This commit is contained in:
Nathan Harold
2018-04-25 12:01:34 -07:00
committed by android-build-merger
3 changed files with 184 additions and 61 deletions

View File

@@ -20,12 +20,16 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.AndroidException;
import android.util.Log;
@@ -172,11 +176,16 @@ public final class IpSecManager {
public void close() {
try {
mService.releaseSecurityParameterIndex(mResourceId);
mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (Exception e) {
// On close we swallow all random exceptions since failure to close is not
// actionable by the user.
Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
} finally {
mResourceId = INVALID_RESOURCE_ID;
mCloseGuard.close();
}
mCloseGuard.close();
}
/** Check that the SPI was closed properly. */
@@ -227,7 +236,6 @@ public final class IpSecManager {
throw new RuntimeException(
"Invalid Resource ID returned by IpSecService: " + status);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -239,6 +247,17 @@ public final class IpSecManager {
public int getResourceId() {
return mResourceId;
}
@Override
public String toString() {
return new StringBuilder()
.append("SecurityParameterIndex{spi=")
.append(mSpi)
.append(",resourceId=")
.append(mResourceId)
.append("}")
.toString();
}
}
/**
@@ -261,7 +280,11 @@ public final class IpSecManager {
mService,
destinationAddress,
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
} catch (ServiceSpecificException e) {
throw rethrowUncheckedExceptionFromServiceSpecificException(e);
} catch (SpiUnavailableException unlikely) {
// Because this function allocates a totally random SPI, it really shouldn't ever
// fail to allocate an SPI; we simply need this because the exception is checked.
throw new ResourceUnavailableException("No SPIs available");
}
}
@@ -274,8 +297,8 @@ public final class IpSecManager {
*
* @param destinationAddress the destination address for traffic bearing the requested SPI.
* For inbound traffic, the destination should be an address currently assigned on-device.
* @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is
* reserved and may not be used. See RFC 4303 Section 2.1.
* @param requestedSpi the requested SPI. The range 1-255 is reserved and may not be used. See
* RFC 4303 Section 2.1.
* @return the reserved SecurityParameterIndex
* @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
* currently allocated for this user
@@ -289,7 +312,11 @@ public final class IpSecManager {
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
}
return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
try {
return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
} catch (ServiceSpecificException e) {
throw rethrowUncheckedExceptionFromServiceSpecificException(e);
}
}
/**
@@ -424,6 +451,8 @@ public final class IpSecManager {
// constructor takes control and closes the user's FD when we exit the method.
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -482,6 +511,8 @@ public final class IpSecManager {
public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
mService.removeTransportModeTransforms(pfd);
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -575,6 +606,13 @@ public final class IpSecManager {
mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (Exception e) {
// On close we swallow all random exceptions since failure to close is not
// actionable by the user.
Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
} finally {
mResourceId = INVALID_RESOURCE_ID;
mCloseGuard.close();
}
try {
@@ -583,7 +621,6 @@ public final class IpSecManager {
Log.e(TAG, "Failed to close UDP Encapsulation Socket with Port= " + mPort);
throw e;
}
mCloseGuard.close();
}
/** Check that the socket was closed properly. */
@@ -600,6 +637,17 @@ public final class IpSecManager {
public int getResourceId() {
return mResourceId;
}
@Override
public String toString() {
return new StringBuilder()
.append("UdpEncapsulationSocket{port=")
.append(mPort)
.append(",resourceId=")
.append(mResourceId)
.append("}")
.toString();
}
};
/**
@@ -627,7 +675,11 @@ public final class IpSecManager {
if (port == 0) {
throw new IllegalArgumentException("Specified port must be a valid port number!");
}
return new UdpEncapsulationSocket(mService, port);
try {
return new UdpEncapsulationSocket(mService, port);
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
}
}
/**
@@ -650,7 +702,11 @@ public final class IpSecManager {
@NonNull
public UdpEncapsulationSocket openUdpEncapsulationSocket()
throws IOException, ResourceUnavailableException {
return new UdpEncapsulationSocket(mService, 0);
try {
return new UdpEncapsulationSocket(mService, 0);
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
}
}
/**
@@ -665,6 +721,7 @@ public final class IpSecManager {
* to create Network objects which are accessible to the Android system.
* @hide
*/
@SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -691,11 +748,14 @@ public final class IpSecManager {
* @param prefixLen length of the InetAddress prefix
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
try {
mService.addAddressToTunnelInterface(
mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -710,11 +770,14 @@ public final class IpSecManager {
* @param prefixLen length of the InetAddress prefix
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
try {
mService.removeAddressFromTunnelInterface(
mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -767,11 +830,16 @@ public final class IpSecManager {
public void close() {
try {
mService.deleteTunnelInterface(mResourceId, mOpPackageName);
mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (Exception e) {
// On close we swallow all random exceptions since failure to close is not
// actionable by the user.
Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
} finally {
mResourceId = INVALID_RESOURCE_ID;
mCloseGuard.close();
}
mCloseGuard.close();
}
/** Check that the Interface was closed properly. */
@@ -788,6 +856,17 @@ public final class IpSecManager {
public int getResourceId() {
return mResourceId;
}
@Override
public String toString() {
return new StringBuilder()
.append("IpSecTunnelInterface{ifname=")
.append(mInterfaceName)
.append(",resourceId=")
.append(mResourceId)
.append("}")
.toString();
}
}
/**
@@ -805,13 +884,18 @@ public final class IpSecManager {
* @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
@NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
throws ResourceUnavailableException, IOException {
return new IpSecTunnelInterface(
mContext, mService, localAddress, remoteAddress, underlyingNetwork);
try {
return new IpSecTunnelInterface(
mContext, mService, localAddress, remoteAddress, underlyingNetwork);
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
}
}
/**
@@ -831,6 +915,7 @@ public final class IpSecManager {
* layer failure.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
@PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
@@ -838,6 +923,8 @@ public final class IpSecManager {
mService.applyTunnelModeTransform(
tunnel.getResourceId(), direction,
transform.getResourceId(), mContext.getOpPackageName());
} catch (ServiceSpecificException e) {
throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -853,4 +940,44 @@ public final class IpSecManager {
mContext = ctx;
mService = checkNotNull(service, "missing service");
}
private static void maybeHandleServiceSpecificException(ServiceSpecificException sse) {
// OsConstants are late binding, so switch statements can't be used.
if (sse.errorCode == OsConstants.EINVAL) {
throw new IllegalArgumentException(sse);
} else if (sse.errorCode == OsConstants.EAGAIN) {
throw new IllegalStateException(sse);
} else if (sse.errorCode == OsConstants.EOPNOTSUPP) {
throw new UnsupportedOperationException(sse);
}
}
/**
* Convert an Errno SSE to the correct Unchecked exception type.
*
* This method never actually returns.
*/
// package
static RuntimeException
rethrowUncheckedExceptionFromServiceSpecificException(ServiceSpecificException sse) {
maybeHandleServiceSpecificException(sse);
throw new RuntimeException(sse);
}
/**
* Convert an Errno SSE to the correct Checked or Unchecked exception type.
*
* This method may throw IOException, or it may throw an unchecked exception; it will never
* actually return.
*/
// package
static IOException rethrowCheckedExceptionFromServiceSpecificException(
ServiceSpecificException sse) throws IOException {
// First see if this is an unchecked exception of a type we know.
// If so, then we prefer the unchecked (specific) type of exception.
maybeHandleServiceSpecificException(sse);
// If not, then all we can do is provide the SSE in the form of an IOException.
throw new ErrnoException(
"IpSec encountered errno=" + sse.errorCode, sse.errorCode).rethrowAsIOException();
}
}

View File

@@ -22,12 +22,14 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -136,6 +138,8 @@ public final class IpSecTransform implements AutoCloseable {
mResourceId = result.resourceId;
Log.d(TAG, "Added Transform with Id " + mResourceId);
mCloseGuard.open("build");
} catch (ServiceSpecificException e) {
throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -180,6 +184,10 @@ public final class IpSecTransform implements AutoCloseable {
stopNattKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} catch (Exception e) {
// On close we swallow all random exceptions since failure to close is not
// actionable by the user.
Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
} finally {
mResourceId = INVALID_RESOURCE_ID;
mCloseGuard.close();
@@ -249,6 +257,7 @@ public final class IpSecTransform implements AutoCloseable {
*
* @hide
*/
@SystemApi
public static class NattKeepaliveCallback {
/** The specified {@code Network} is not connected. */
public static final int ERROR_INVALID_NETWORK = 1;
@@ -279,6 +288,7 @@ public final class IpSecTransform implements AutoCloseable {
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -321,6 +331,7 @@ public final class IpSecTransform implements AutoCloseable {
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -473,6 +484,7 @@ public final class IpSecTransform implements AutoCloseable {
* @throws IOException indicating other errors
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTransform buildTunnelModeTransform(
@@ -502,4 +514,13 @@ public final class IpSecTransform implements AutoCloseable {
mConfig = new IpSecConfig();
}
}
@Override
public String toString() {
return new StringBuilder()
.append("IpSecTransform{resourceId=")
.append(mResourceId)
.append("}")
.toString();
}
}