Merge changes from topic "vpnmanager-events-T" into tm-dev

* changes:
  Address leftover comments of ag/18108411
  Address leftover comments of ag/18112116
  Rename vars of Ikev2VpnRunner
  Send VPN manager event when there is a network error
  Send VPN manager event when there is an IkeProtocolException
This commit is contained in:
Lorenzo Colitti
2022-06-02 10:12:53 +00:00
committed by Android (Google) Code Review
2 changed files with 194 additions and 45 deletions

View File

@@ -88,7 +88,10 @@ import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
import android.net.ipsec.ike.exceptions.IkeNonProtocolException;
import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.ipsec.ike.exceptions.IkeTimeoutException;
import android.os.Binder; import android.os.Binder;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
@@ -760,31 +763,36 @@ public class Vpn {
// Also notify the new package if there was a provider change. // Also notify the new package if there was a provider change.
final boolean shouldNotifyNewPkg = isVpnApp(packageName) && isPackageChanged; final boolean shouldNotifyNewPkg = isVpnApp(packageName) && isPackageChanged;
if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) { if (!setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) {
saveAlwaysOnPackage(); return false;
// TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from }
// ConnectivityServiceTest.
if (shouldNotifyOldPkg && SdkLevel.isAtLeastT()) { saveAlwaysOnPackage();
// If both of shouldNotifyOldPkg & isPackageChanged are true, which means the
// always-on of old package is disabled or the old package is replaced with the new // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
// package. In this case, VpnProfileState should be disconnected. // ConnectivityServiceTest.
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, if (!SdkLevel.isAtLeastT()) {
-1 /* errorClass */, -1 /* errorCode*/, oldPackage,
null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState()
: makeVpnProfileStateLocked(),
null /* underlyingNetwork */, null /* nc */, null /* lp */);
}
// TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
// ConnectivityServiceTest.
if (shouldNotifyNewPkg && SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED,
-1 /* errorClass */, -1 /* errorCode*/, packageName,
getSessionKeyLocked(), makeVpnProfileStateLocked(),
null /* underlyingNetwork */, null /* nc */, null /* lp */);
}
return true; return true;
} }
return false;
if (shouldNotifyOldPkg) {
// If both of shouldNotifyOldPkg & isPackageChanged are true, that means the
// always-on of old package is disabled or the old package is replaced with the new
// package. In this case, VpnProfileState should be disconnected.
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED,
-1 /* errorClass */, -1 /* errorCode*/, oldPackage,
null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState()
: makeVpnProfileStateLocked(),
null /* underlyingNetwork */, null /* nc */, null /* lp */);
}
if (shouldNotifyNewPkg) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED,
-1 /* errorClass */, -1 /* errorCode*/, packageName,
getSessionKeyLocked(), makeVpnProfileStateLocked(),
null /* underlyingNetwork */, null /* nc */, null /* lp */);
}
return true;
} }
/** /**
@@ -2530,6 +2538,21 @@ public class Vpn {
} }
} }
@Nullable
protected synchronized NetworkCapabilities getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
NetworkCapabilities nc) {
if (nc == null) return null;
return mConnectivityManager.getRedactedNetworkCapabilitiesForPackage(
nc, mOwnerUID, mPackage);
}
@Nullable
protected synchronized LinkProperties getRedactedLinkPropertiesOfUnderlyingNetwork(
LinkProperties lp) {
if (lp == null) return null;
return mConnectivityManager.getRedactedLinkPropertiesForPackage(lp, mOwnerUID, mPackage);
}
/** This class represents the common interface for all VPN runners. */ /** This class represents the common interface for all VPN runners. */
@VisibleForTesting @VisibleForTesting
abstract class VpnRunner extends Thread { abstract class VpnRunner extends Thread {
@@ -2564,6 +2587,10 @@ public class Vpn {
interface IkeV2VpnRunnerCallback { interface IkeV2VpnRunnerCallback {
void onDefaultNetworkChanged(@NonNull Network network); void onDefaultNetworkChanged(@NonNull Network network);
void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc);
void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp);
void onChildOpened( void onChildOpened(
@NonNull Network network, @NonNull ChildSessionConfiguration childConfig); @NonNull Network network, @NonNull ChildSessionConfiguration childConfig);
@@ -2620,14 +2647,14 @@ public class Vpn {
@Nullable private IpSecTunnelInterface mTunnelIface; @Nullable private IpSecTunnelInterface mTunnelIface;
@Nullable private IkeSession mSession; @Nullable private IkeSession mSession;
@Nullable private Network mActiveNetwork; @Nullable private Network mActiveNetwork;
@Nullable private NetworkCapabilities mUnderlyingNetworkCapabilities;
@Nullable private LinkProperties mUnderlyingLinkProperties;
private final String mSessionKey; private final String mSessionKey;
IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) { IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
super(TAG); super(TAG);
mProfile = profile; mProfile = profile;
mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
// Pass mExecutor into Ikev2VpnNetworkCallback and make sure that IkeV2VpnRunnerCallback
// will be called by the mExecutor thread.
mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor); mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor);
mSessionKey = UUID.randomUUID().toString(); mSessionKey = UUID.randomUUID().toString();
} }
@@ -2849,6 +2876,16 @@ public class Vpn {
} }
} }
/** Called when the NetworkCapabilities of underlying network is changed */
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
mUnderlyingNetworkCapabilities = nc;
}
/** Called when the LinkProperties of underlying network is changed */
public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) {
mUnderlyingLinkProperties = lp;
}
/** Marks the state as FAILED, and disconnects. */ /** Marks the state as FAILED, and disconnects. */
private void markFailedAndDisconnect(Exception exception) { private void markFailedAndDisconnect(Exception exception) {
synchronized (Vpn.this) { synchronized (Vpn.this) {
@@ -2879,28 +2916,120 @@ public class Vpn {
return; return;
} }
if (exception instanceof IkeProtocolException) { synchronized (Vpn.this) {
final IkeProtocolException ikeException = (IkeProtocolException) exception; if (exception instanceof IkeProtocolException) {
final IkeProtocolException ikeException = (IkeProtocolException) exception;
switch (ikeException.getErrorType()) { switch (ikeException.getErrorType()) {
case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough
case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough
case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough
case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough
case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough
case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE: case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE:
// All the above failures are configuration errors, and are terminal // All the above failures are configuration errors, and are terminal
markFailedAndDisconnect(exception); // TODO(b/230548427): Remove SDK check once VPN related stuff are
return; // decoupled from ConnectivityServiceTest.
// All other cases possibly recoverable. if (SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR,
VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
ikeException.getErrorType(),
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
mUnderlyingLinkProperties));
}
markFailedAndDisconnect(exception);
return;
// All other cases possibly recoverable.
default:
// All the above failures are configuration errors, and are terminal
// TODO(b/230548427): Remove SDK check once VPN related stuff are
// decoupled from ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR,
VpnManager.ERROR_CLASS_RECOVERABLE,
ikeException.getErrorType(),
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
mUnderlyingLinkProperties));
}
}
} else if (exception instanceof IllegalArgumentException) {
// Failed to build IKE/ChildSessionParams; fatal profile configuration error
markFailedAndDisconnect(exception);
return;
} else if (exception instanceof IkeNetworkLostException) {
// TODO(b/230548427): Remove SDK check once VPN related stuff are
// decoupled from ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_LOST,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
mUnderlyingLinkProperties));
}
} else if (exception instanceof IkeNonProtocolException) {
if (exception.getCause() instanceof UnknownHostException) {
// TODO(b/230548427): Remove SDK check once VPN related stuff are
// decoupled from ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
mUnderlyingLinkProperties));
}
} else if (exception.getCause() instanceof IkeTimeoutException) {
// TODO(b/230548427): Remove SDK check once VPN related stuff are
// decoupled from ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
mUnderlyingLinkProperties));
}
} else if (exception.getCause() instanceof IOException) {
// TODO(b/230548427): Remove SDK check once VPN related stuff are
// decoupled from ConnectivityServiceTest.
if (SdkLevel.isAtLeastT()) {
sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_IO,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
mUnderlyingLinkProperties));
}
}
} else if (exception != null) {
Log.wtf(TAG, "onSessionLost: exception = " + exception);
} }
} else if (exception instanceof IllegalArgumentException) {
// Failed to build IKE/ChildSessionParams; fatal profile configuration error
markFailedAndDisconnect(exception);
return;
} }
mActiveNetwork = null; mActiveNetwork = null;
mUnderlyingNetworkCapabilities = null;
mUnderlyingLinkProperties = null;
// Close all obsolete state, but keep VPN alive incase a usable network comes up. // Close all obsolete state, but keep VPN alive incase a usable network comes up.
// (Mirrors VpnService behavior) // (Mirrors VpnService behavior)
@@ -2965,6 +3094,8 @@ public class Vpn {
*/ */
private void disconnectVpnRunner() { private void disconnectVpnRunner() {
mActiveNetwork = null; mActiveNetwork = null;
mUnderlyingNetworkCapabilities = null;
mUnderlyingLinkProperties = null;
mIsRunning = false; mIsRunning = false;
resetIkeState(); resetIkeState();

View File

@@ -50,7 +50,9 @@ import android.net.InetAddresses;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.net.IpSecAlgorithm; import android.net.IpSecAlgorithm;
import android.net.IpSecTransform; import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.eap.EapSessionConfig; import android.net.eap.EapSessionConfig;
import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.ChildSaProposal;
@@ -86,7 +88,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executor;
/** /**
* Utility class to build and convert IKEv2/IPsec parameters. * Utility class to build and convert IKEv2/IPsec parameters.
@@ -377,10 +379,10 @@ public class VpnIkev2Utils {
static class Ikev2VpnNetworkCallback extends NetworkCallback { static class Ikev2VpnNetworkCallback extends NetworkCallback {
private final String mTag; private final String mTag;
private final Vpn.IkeV2VpnRunnerCallback mCallback; private final Vpn.IkeV2VpnRunnerCallback mCallback;
private final ExecutorService mExecutor; private final Executor mExecutor;
Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback, Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback,
ExecutorService executor) { Executor executor) {
mTag = tag; mTag = tag;
mCallback = callback; mCallback = callback;
mExecutor = executor; mExecutor = executor;
@@ -392,6 +394,22 @@ public class VpnIkev2Utils {
mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network)); mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
} }
@Override
public void onCapabilitiesChanged(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities) {
Log.d(mTag, "NC changed for net " + network + " : " + networkCapabilities);
mExecutor.execute(
() -> mCallback.onDefaultNetworkCapabilitiesChanged(networkCapabilities));
}
@Override
public void onLinkPropertiesChanged(@NonNull Network network,
@NonNull LinkProperties linkProperties) {
Log.d(mTag, "LP changed for net " + network + " : " + linkProperties);
mExecutor.execute(
() -> mCallback.onDefaultNetworkLinkPropertiesChanged(linkProperties));
}
@Override @Override
public void onLost(@NonNull Network network) { public void onLost(@NonNull Network network) {
Log.d(mTag, "Tearing down; lost network: " + network); Log.d(mTag, "Tearing down; lost network: " + network);