From d12d2011cb62a6c26a052b3a91c2d4679c9e2e7b Mon Sep 17 00:00:00 2001 From: chiachangwang Date: Thu, 10 Nov 2022 10:25:25 +0000 Subject: [PATCH] Expose vpn bypassability in VpnTransportInfo Bug: 256775913 Test: m framework-connectivity.stubs.source.system-update-current-api Test: m lint-check Test: atest FrameworksNetTests Change-Id: I6aaad7b4372aa4f91df4bed89caea674031a6367 --- framework/api/module-lib-current.txt | 6 +- framework/api/system-current.txt | 9 +++ .../src/android/net/VpnTransportInfo.java | 64 +++++++++++++++++-- .../android/net/VpnTransportInfoTest.java | 55 +++++++++++++--- 4 files changed, 113 insertions(+), 21 deletions(-) diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt index a2a1ac0992..988947a3e0 100644 --- a/framework/api/module-lib-current.txt +++ b/framework/api/module-lib-current.txt @@ -228,13 +228,9 @@ package android.net { } public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { - ctor public VpnTransportInfo(int, @Nullable String); - method public int describeContents(); + ctor @Deprecated public VpnTransportInfo(int, @Nullable String); method @Nullable public String getSessionId(); - method public int getType(); method @NonNull public android.net.VpnTransportInfo makeCopy(long); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; } } diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 8b3519727b..c7872a0c73 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -511,6 +511,15 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { + ctor public VpnTransportInfo(int, @Nullable String, boolean); + method public int describeContents(); + method public boolean getBypassable(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + } package android.net.apf { diff --git a/framework/src/android/net/VpnTransportInfo.java b/framework/src/android/net/VpnTransportInfo.java index 4071c9ab71..ebad47799f 100644 --- a/framework/src/android/net/VpnTransportInfo.java +++ b/framework/src/android/net/VpnTransportInfo.java @@ -17,6 +17,7 @@ package android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static android.annotation.SystemApi.Client.PRIVILEGED_APPS; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import android.annotation.NonNull; @@ -27,6 +28,10 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import androidx.annotation.RequiresApi; + +import com.android.modules.utils.build.SdkLevel; + import java.util.Objects; /** @@ -37,7 +42,7 @@ import java.util.Objects; * * @hide */ -@SystemApi(client = MODULE_LIBRARIES) +@SystemApi(client = PRIVILEGED_APPS) public final class VpnTransportInfo implements TransportInfo, Parcelable { /** Type of this VPN. */ private final int mType; @@ -45,6 +50,13 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { @Nullable private final String mSessionId; + private final boolean mBypassable; + + // TODO: Refer to Build.VERSION_CODES when it's available in every branch. + private static final int UPSIDE_DOWN_CAKE = 34; + + /** @hide */ + @SystemApi(client = MODULE_LIBRARIES) @Override public @RedactionType long getApplicableRedactions() { return REDACT_FOR_NETWORK_SETTINGS; @@ -52,21 +64,58 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { /** * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary. + * @hide */ @NonNull + @SystemApi(client = MODULE_LIBRARIES) public VpnTransportInfo makeCopy(@RedactionType long redactions) { return new VpnTransportInfo(mType, - ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : mSessionId); + ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : mSessionId, mBypassable); } + /** + * @deprecated please use {@link VpnTransportInfo(int,String,boolean)} instead. + * @hide + */ + @Deprecated + @SystemApi(client = MODULE_LIBRARIES) public VpnTransportInfo(int type, @Nullable String sessionId) { + // When the module runs on older SDKs, |bypassable| will always be false since the old Vpn + // code will call this constructor. For Settings VPNs, this is always correct as they are + // never bypassable. For VpnManager and VpnService types, this may be wrong since both of + // them have a choice. However, on these SDKs VpnTransportInfo#getBypassable is not + // available anyway, so this should be harmless. False is a better choice than true here + // regardless because it is the default value for both VpnManager and VpnService if the app + // does not do anything about it. + this(type, sessionId, false /* bypassable */); + } + + public VpnTransportInfo(int type, @Nullable String sessionId, boolean bypassable) { this.mType = type; this.mSessionId = sessionId; + this.mBypassable = bypassable; + } + + /** + * Returns whether the VPN is allowing bypass. + * + * This method is not supported in SDK below U, and will throw + * {@code UnsupportedOperationException} if called. + */ + @RequiresApi(UPSIDE_DOWN_CAKE) + public boolean getBypassable() { + if (!SdkLevel.isAtLeastU()) { + throw new UnsupportedOperationException("Not supported before U"); + } + + return mBypassable; } /** * Returns the session Id of this VpnTransportInfo. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) @Nullable public String getSessionId() { return mSessionId; @@ -84,17 +133,19 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { if (!(o instanceof VpnTransportInfo)) return false; VpnTransportInfo that = (VpnTransportInfo) o; - return (this.mType == that.mType) && TextUtils.equals(this.mSessionId, that.mSessionId); + return (this.mType == that.mType) && TextUtils.equals(this.mSessionId, that.mSessionId) + && (this.mBypassable == that.mBypassable); } @Override public int hashCode() { - return Objects.hash(mType, mSessionId); + return Objects.hash(mType, mSessionId, mBypassable); } @Override public String toString() { - return String.format("VpnTransportInfo{type=%d, sessionId=%s}", mType, mSessionId); + return String.format("VpnTransportInfo{type=%d, sessionId=%s, bypassable=%b}", + mType, mSessionId, mBypassable); } @Override @@ -106,12 +157,13 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeString(mSessionId); + dest.writeBoolean(mBypassable); } public static final @NonNull Creator CREATOR = new Creator() { public VpnTransportInfo createFromParcel(Parcel in) { - return new VpnTransportInfo(in.readInt(), in.readString()); + return new VpnTransportInfo(in.readInt(), in.readString(), in.readBoolean()); } public VpnTransportInfo[] newArray(int size) { return new VpnTransportInfo[size]; diff --git a/tests/unit/java/android/net/VpnTransportInfoTest.java b/tests/unit/java/android/net/VpnTransportInfoTest.java index b4c7ac4aa6..05102096de 100644 --- a/tests/unit/java/android/net/VpnTransportInfoTest.java +++ b/tests/unit/java/android/net/VpnTransportInfoTest.java @@ -19,10 +19,13 @@ package android.net; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import static android.net.NetworkCapabilities.REDACT_NONE; +import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import android.os.Build; @@ -31,6 +34,7 @@ import androidx.test.filters.SmallTest; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,36 +42,67 @@ import org.junit.runner.RunWith; @SmallTest @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) public class VpnTransportInfoTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); @Test public void testParceling() { - VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345"); - assertParcelSane(v, 2 /* fieldCount */); + final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345"); + assertParcelSane(v, 3 /* fieldCount */); + + final VpnTransportInfo v2 = + new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345", true); + assertParcelSane(v2, 3 /* fieldCount */); } @Test public void testEqualsAndHashCode() { String session1 = "12345"; String session2 = "6789"; - VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); - VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1); - VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); - VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1); - VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1); - VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2); + final VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); + final VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1); + final VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); + final VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1); + final VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1); + final VpnTransportInfo v16 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1, true); + final VpnTransportInfo v17 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1, true); + final VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2); - VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS); - VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS); + final VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS); + final VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS); + final VpnTransportInfo v33 = v16.makeCopy(REDACT_FOR_NETWORK_SETTINGS); + final VpnTransportInfo v34 = v17.makeCopy(REDACT_FOR_NETWORK_SETTINGS); assertNotEquals(v11, v12); assertNotEquals(v13, v14); assertNotEquals(v14, v15); assertNotEquals(v14, v21); + assertNotEquals(v15, v16); assertEquals(v11, v13); assertEquals(v31, v32); + assertEquals(v33, v34); assertEquals(v11.hashCode(), v13.hashCode()); + assertEquals(v16.hashCode(), v17.hashCode()); assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions()); assertEquals(session1, v15.makeCopy(REDACT_NONE).getSessionId()); } + + @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU) + @Test + public void testGetBypassable_beforeU() { + final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345"); + assertThrows(UnsupportedOperationException.class, () -> v.getBypassable()); + } + + @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @Test + public void testGetBypassable_afterU() { + final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345"); + assertFalse(v.getBypassable()); + + final VpnTransportInfo v2 = + new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345", true); + assertTrue(v2.getBypassable()); + } }