From b1c997a7018ff279eb716e38cba5f4a75db29eea Mon Sep 17 00:00:00 2001 From: Kuen Yuet Cheung Date: Wed, 8 Feb 2023 14:40:21 +0800 Subject: Add iwlan error classname and first line of stacktrace in metrics Bug: 268290174 Test: statsd_testdrive 519 Change-Id: Ibfe4ba7b65849ad9fb96dcb2eca7c98904be68b1 --- src/com/google/android/iwlan/IwlanDataService.java | 9 ++ .../google/android/iwlan/proto/MetricsAtom.java | 35 ++++++- .../google/android/iwlan/IwlanDataServiceTest.java | 110 +++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java index 8f11d5b..0536540 100644 --- a/src/com/google/android/iwlan/IwlanDataService.java +++ b/src/com/google/android/iwlan/IwlanDataService.java @@ -931,6 +931,12 @@ public class IwlanDataService extends DataService { mMetricsAtomForApn.put(apnName, metricsAtom); } + @VisibleForTesting + @Nullable + public MetricsAtom getMetricsAtomByApn(String apnName) { + return mMetricsAtomForApn.get(apnName); + } + @VisibleForTesting public IwlanTunnelCallback getIwlanTunnelCallback() { return mIwlanTunnelCallback; @@ -1250,6 +1256,9 @@ public class IwlanDataService extends DataService { // Record setup result for the Metrics metricsAtom.setSetupRequestResult(DataServiceCallback.RESULT_SUCCESS); metricsAtom.setIwlanError(iwlanError.getErrorType()); + + metricsAtom.setIwlanErrorWrappedClassnameAndStack(iwlanError); + metricsAtom.setTunnelState(tunnelState.getState()); metricsAtom.setMessageId( IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED); diff --git a/src/com/google/android/iwlan/proto/MetricsAtom.java b/src/com/google/android/iwlan/proto/MetricsAtom.java index 33b0fcd..f127e79 100644 --- a/src/com/google/android/iwlan/proto/MetricsAtom.java +++ b/src/com/google/android/iwlan/proto/MetricsAtom.java @@ -16,6 +16,10 @@ package com.google.android.iwlan.proto; +import android.net.ipsec.ike.exceptions.IkeIOException; +import android.net.ipsec.ike.exceptions.IkeInternalException; + +import com.google.android.iwlan.IwlanError; import com.google.android.iwlan.IwlanStatsLog; public class MetricsAtom { @@ -37,6 +41,7 @@ public class MetricsAtom { private int mHandoverFailureMode; private int mRetryDurationMillis; private int mWifiSignalValue; + private String mIwlanErrorWrappedClassnameAndStack; public void setMessageId(int messageId) { this.mMessageId = messageId; @@ -110,6 +115,33 @@ public class MetricsAtom { this.mWifiSignalValue = wifiSignalValue; } + public void setIwlanErrorWrappedClassnameAndStack(IwlanError iwlanError) { + Throwable iwlanErrorWrapped = iwlanError.getException(); + if (iwlanErrorWrapped instanceof IkeInternalException + || iwlanErrorWrapped instanceof IkeIOException) { + iwlanErrorWrapped = iwlanErrorWrapped.getCause(); + } + + if (iwlanErrorWrapped == null) { + this.mIwlanErrorWrappedClassnameAndStack = null; + return; + } + + StackTraceElement[] iwlanErrorWrappedStackTraceElements = iwlanErrorWrapped.getStackTrace(); + String iwlanErrorWrappedFirstLineStrackTrace = + iwlanErrorWrappedStackTraceElements.length == 0 + ? "" + : "\n" + iwlanErrorWrappedStackTraceElements[0].toString(); + + this.mIwlanErrorWrappedClassnameAndStack = + iwlanErrorWrapped.getClass().getCanonicalName() + + iwlanErrorWrappedFirstLineStrackTrace; + } + + public String getIwlanErrorWrappedClassnameAndStack() { + return mIwlanErrorWrappedClassnameAndStack; + } + public void sendMetricsData() { if (mMessageId == IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED) { IwlanStatsLog.write( @@ -129,7 +161,8 @@ public class MetricsAtom { mIkeTunnelEstablishmentDurationMillis, mTunnelState, mHandoverFailureMode, - mRetryDurationMillis); + mRetryDurationMillis, + mIwlanErrorWrappedClassnameAndStack); return; } else if (mMessageId == IwlanStatsLog.IWLAN_PDN_DISCONNECTED_REASON_REPORTED) { IwlanStatsLog.write( diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java index cccbb7c..dd0dc28 100644 --- a/test/com/google/android/iwlan/IwlanDataServiceTest.java +++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java @@ -48,6 +48,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; +import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.vcn.VcnTransportInfo; import android.os.test.TestLooper; import android.telephony.AccessNetworkConstants.AccessNetworkType; @@ -73,6 +74,7 @@ import com.google.android.iwlan.epdg.EpdgTunnelManager; import com.google.android.iwlan.epdg.TunnelLinkProperties; import com.google.android.iwlan.epdg.TunnelLinkPropertiesTest; import com.google.android.iwlan.epdg.TunnelSetupRequest; +import com.google.android.iwlan.proto.MetricsAtom; import org.junit.After; import org.junit.Before; @@ -1364,6 +1366,114 @@ public class IwlanDataServiceTest { mIwlanDataService.onUnbind(null); } + @Test + public void testMetricsWhenTunnelClosedWithWrappedException() { + DataProfile dp = buildImsDataProfile(); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_IN_BRINGUP, + null, /* linkProperties */ + false /* isHandover */, + 1 /* pduSessionId */, + true /* isImsOrEmergency */); + + mSpyIwlanDataServiceProvider.setMetricsAtom( + TEST_APN_NAME, + 64, // type IMS + true, + 13, // LTE + false, + true, + 1 // Transport Wi-Fi + ); + + MetricsAtom metricsAtom = mSpyIwlanDataServiceProvider.getMetricsAtomByApn(TEST_APN_NAME); + assertNotNull(metricsAtom); + + String exceptionMessage = "Some exception message"; + Exception mockException = spy(new IllegalStateException(exceptionMessage)); + String firstDeclaringClassName = "test.test.TestClass"; + String firstMethodName = "someMethod"; + String firstFileName = "TestClass.java"; + int firstLineNumber = 12345; + StackTraceElement[] stackTraceElements = { + new StackTraceElement( + firstDeclaringClassName, firstMethodName, firstFileName, firstLineNumber), + new StackTraceElement("test", "test", "test.java", 123) + }; + doReturn(stackTraceElements).when(mockException).getStackTrace(); + + when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX))) + .thenReturn(mMockErrorPolicyManager); + when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME))) + .thenReturn(DataFailCause.ERROR_UNSPECIFIED); + + mSpyIwlanDataServiceProvider + .getIwlanTunnelCallback() + .onClosed(TEST_APN_NAME, new IwlanError(new IkeInternalException(mockException))); + + mTestLooper.dispatchAll(); + + var expectedClassNameAndStack = + mockException.getClass().getCanonicalName() + + "\n" + + firstDeclaringClassName + + "." + + firstMethodName + + "(" + + firstFileName + + ":" + + firstLineNumber + + ")"; + + assertEquals( + expectedClassNameAndStack, metricsAtom.getIwlanErrorWrappedClassnameAndStack()); + } + + @Test + public void testMetricsWhenTunnelClosedWithoutWrappedException() { + DataProfile dp = buildImsDataProfile(); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_IN_BRINGUP, + null, /* linkProperties */ + false /* isHandover */, + 1 /* pduSessionId */, + true /* isImsOrEmergency */); + + mSpyIwlanDataServiceProvider.setMetricsAtom( + TEST_APN_NAME, + 64, // type IMS + true, + 13, // LTE + false, + true, + 1 // Transport Wi-Fi + ); + + MetricsAtom metricsAtom = mSpyIwlanDataServiceProvider.getMetricsAtomByApn(TEST_APN_NAME); + assertNotNull(metricsAtom); + + when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX))) + .thenReturn(mMockErrorPolicyManager); + when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME))) + .thenReturn(DataFailCause.ERROR_UNSPECIFIED); + + mSpyIwlanDataServiceProvider + .getIwlanTunnelCallback() + .onClosed( + TEST_APN_NAME, + new IwlanError(IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED)); + + mTestLooper.dispatchAll(); + + assertEquals(null, metricsAtom.getIwlanErrorWrappedClassnameAndStack()); + } + private void mockTunnelSetupFail(DataProfile dp) { mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ -- cgit v1.2.3 From 672cc1cb585272592a0b2e54d95dbf79f84763fe Mon Sep 17 00:00:00 2001 From: Kuen Yuet Cheung Date: Fri, 10 Mar 2023 21:42:52 +0800 Subject: Split iwlan error classname and first line of stacktrace in metrics Bug: 268290174 Test: statsd_testdrive 519 Change-Id: Iea2de7ff55a64b035bb0292bed9a1b4e8ca244c4 --- .../google/android/iwlan/proto/MetricsAtom.java | 29 +++++++++++++--------- .../google/android/iwlan/IwlanDataServiceTest.java | 14 ++++++----- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/com/google/android/iwlan/proto/MetricsAtom.java b/src/com/google/android/iwlan/proto/MetricsAtom.java index f127e79..7ecf464 100644 --- a/src/com/google/android/iwlan/proto/MetricsAtom.java +++ b/src/com/google/android/iwlan/proto/MetricsAtom.java @@ -41,7 +41,8 @@ public class MetricsAtom { private int mHandoverFailureMode; private int mRetryDurationMillis; private int mWifiSignalValue; - private String mIwlanErrorWrappedClassnameAndStack; + private String mIwlanErrorWrappedClassname; + private String mIwlanErrorWrappedStackFirstFrame; public void setMessageId(int messageId) { this.mMessageId = messageId; @@ -123,23 +124,26 @@ public class MetricsAtom { } if (iwlanErrorWrapped == null) { - this.mIwlanErrorWrappedClassnameAndStack = null; + this.mIwlanErrorWrappedClassname = null; + this.mIwlanErrorWrappedStackFirstFrame = null; return; } + this.mIwlanErrorWrappedClassname = iwlanErrorWrapped.getClass().getCanonicalName(); + StackTraceElement[] iwlanErrorWrappedStackTraceElements = iwlanErrorWrapped.getStackTrace(); - String iwlanErrorWrappedFirstLineStrackTrace = - iwlanErrorWrappedStackTraceElements.length == 0 - ? "" - : "\n" + iwlanErrorWrappedStackTraceElements[0].toString(); + this.mIwlanErrorWrappedStackFirstFrame = + iwlanErrorWrappedStackTraceElements.length != 0 + ? iwlanErrorWrappedStackTraceElements[0].toString() + : null; + } - this.mIwlanErrorWrappedClassnameAndStack = - iwlanErrorWrapped.getClass().getCanonicalName() - + iwlanErrorWrappedFirstLineStrackTrace; + public String getIwlanErrorWrappedClassname() { + return mIwlanErrorWrappedClassname; } - public String getIwlanErrorWrappedClassnameAndStack() { - return mIwlanErrorWrappedClassnameAndStack; + public String getIwlanErrorWrappedStackFirstFrame() { + return mIwlanErrorWrappedStackFirstFrame; } public void sendMetricsData() { @@ -162,7 +166,8 @@ public class MetricsAtom { mTunnelState, mHandoverFailureMode, mRetryDurationMillis, - mIwlanErrorWrappedClassnameAndStack); + mIwlanErrorWrappedClassname, + mIwlanErrorWrappedStackFirstFrame); return; } else if (mMessageId == IwlanStatsLog.IWLAN_PDN_DISCONNECTED_REASON_REPORTED) { IwlanStatsLog.write( diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java index dd0dc28..5e028d6 100644 --- a/test/com/google/android/iwlan/IwlanDataServiceTest.java +++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java @@ -1416,10 +1416,8 @@ public class IwlanDataServiceTest { mTestLooper.dispatchAll(); - var expectedClassNameAndStack = - mockException.getClass().getCanonicalName() - + "\n" - + firstDeclaringClassName + var expectedStackFirstFrame = + firstDeclaringClassName + "." + firstMethodName + "(" @@ -1429,7 +1427,10 @@ public class IwlanDataServiceTest { + ")"; assertEquals( - expectedClassNameAndStack, metricsAtom.getIwlanErrorWrappedClassnameAndStack()); + mockException.getClass().getCanonicalName(), + metricsAtom.getIwlanErrorWrappedClassname()); + + assertEquals(expectedStackFirstFrame, metricsAtom.getIwlanErrorWrappedStackFirstFrame()); } @Test @@ -1471,7 +1472,8 @@ public class IwlanDataServiceTest { mTestLooper.dispatchAll(); - assertEquals(null, metricsAtom.getIwlanErrorWrappedClassnameAndStack()); + assertEquals(null, metricsAtom.getIwlanErrorWrappedClassname()); + assertEquals(null, metricsAtom.getIwlanErrorWrappedStackFirstFrame()); } private void mockTunnelSetupFail(DataProfile dp) { -- cgit v1.2.3 From 14b37838dce8058868628f5fadf09b0441e4bf02 Mon Sep 17 00:00:00 2001 From: Kuen Yuet Cheung Date: Sat, 4 Feb 2023 04:04:50 +0800 Subject: Change ePDG plmn list logic and support configure plmn priority Bug: 226926218 Test: atest EpdgSelectorTest Change-Id: If2397ef4db5e7e5756fb95c3a5cccdee97b9dfad --- .../google/android/iwlan/epdg/EpdgSelector.java | 90 ++++++++----- .../android/iwlan/epdg/EpdgSelectorTest.java | 149 +++++++++++++++++---- 2 files changed, 184 insertions(+), 55 deletions(-) diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java index d6bc103..183cd40 100644 --- a/src/com/google/android/iwlan/epdg/EpdgSelector.java +++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java @@ -398,8 +398,6 @@ public class EpdgSelector { } private String[] getPlmnList() { - List plmnsFromSubInfo = new ArrayList<>(); - List plmnsFromCarrierConfig = new ArrayList<>( Arrays.asList( @@ -424,43 +422,52 @@ public class EpdgSelector { return plmnsFromCarrierConfig.toArray(new String[plmnsFromCarrierConfig.size()]); } - // There are three sources of plmns - sim plmn, plmn list from carrier config and - // Ehplmn list from subscription info. - // The plmns are prioritized as follows: - // 1. Sim plmn - // 2. Plmns common to both lists. - // 3. Remaining plmns in the lists. - List combinedList = new ArrayList<>(); // Get MCCMNC from IMSI - String plmnFromImsi = subInfo.getMccString() + "-" + subInfo.getMncString(); - combinedList.add(plmnFromImsi); - - // Get Ehplmns from TelephonyManager - for (String ehplmn : getEhplmns()) { - if (ehplmn.length() == 5 || ehplmn.length() == 6) { - StringBuilder str = new StringBuilder(ehplmn); - str.insert(3, "-"); - plmnsFromSubInfo.add(str.toString()); - } - } + String plmnFromImsi = subInfo.getMccString() + subInfo.getMncString(); - Log.d(TAG, "plmnsFromSubInfo:" + plmnsFromSubInfo); + int[] prioritizedPlmnTypes = + IwlanHelper.getConfig( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + mContext, + mSlotId); - // To avoid double adding plmn from imsi - plmnsFromCarrierConfig.removeIf(i -> i.equals(plmnFromImsi)); - plmnsFromSubInfo.removeIf(i -> i.equals(plmnFromImsi)); + List ehplmns = getEhplmns(); + String registeredPlmn = getRegisteredPlmn(); + boolean isRegisteredPlmnInConfig = + registeredPlmn != null + && plmnsFromCarrierConfig.contains( + new StringBuilder(registeredPlmn).insert(3, "-").toString()); - for (Iterator iterator = plmnsFromCarrierConfig.iterator(); iterator.hasNext(); ) { - String plmn = iterator.next(); - if (plmnsFromSubInfo.contains(plmn)) { - combinedList.add(plmn); - plmnsFromSubInfo.remove(plmn); - iterator.remove(); + List combinedList = new ArrayList<>(); + for (int plmnType : prioritizedPlmnTypes) { + switch (plmnType) { + case CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN: + if (registeredPlmn != null && isRegisteredPlmnInConfig) { + combinedList.add(registeredPlmn); + } + break; + case CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN: + combinedList.add(plmnFromImsi); + break; + case CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL: + combinedList.addAll(getEhplmns()); + break; + case CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_FIRST: + if (!ehplmns.isEmpty()) { + combinedList.add(ehplmns.get(0)); + } + break; + default: + Log.e(TAG, "Unknown PLMN type: " + plmnType); + break; } } - combinedList.addAll(plmnsFromSubInfo); - combinedList.addAll(plmnsFromCarrierConfig); + combinedList = + combinedList.stream() + .distinct() + .map(plmn -> new StringBuilder(plmn).insert(3, "-").toString()) + .toList(); Log.d(TAG, "Final plmn list:" + combinedList); return combinedList.toArray(new String[combinedList.size()]); @@ -499,6 +506,25 @@ public class EpdgSelector { return mccmnc; } + /** + * @return the registered PLMN, null if not registered with 3gpp or failed to get telephony + * manager + */ + @Nullable + private String getRegisteredPlmn() { + TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); + if (telephonyManager == null) { + Log.e(TAG, "TelephonyManager is NULL"); + return null; + } + + telephonyManager = + telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); + + String registeredPlmn = telephonyManager.getNetworkOperator(); + return registeredPlmn.isEmpty() ? null : registeredPlmn; + } + private List getEhplmns() { TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class); mTelephonyManager = diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java index 4a9d5a4..e4ed082 100644 --- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java @@ -80,6 +80,8 @@ public class EpdgSelectorTest { private static final String TEST_IP_ADDRESS_3 = "127.0.0.4"; private static final String TEST_IP_ADDRESS_4 = "127.0.0.5"; private static final String TEST_IP_ADDRESS_5 = "127.0.0.6"; + private static final String TEST_IP_ADDRESS_6 = "127.0.0.7"; + private static final String TEST_IP_ADDRESS_7 = "127.0.0.8"; private static final String TEST_IPV6_ADDRESS = "0000:0000:0000:0000:0000:0000:0000:0001"; private static int testPcoIdIPv6 = 0xFF01; @@ -134,6 +136,8 @@ public class EpdgSelectorTest { when(mMockSubscriptionInfo.getMncString()).thenReturn("120"); + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("311120"); + when(mMockContext.getSystemService(eq(TelephonyManager.class))) .thenReturn(mMockTelephonyManager); @@ -229,11 +233,11 @@ public class EpdgSelectorTest { @Test public void testPlmnResolutionMethodWithNoPlmnInCarrierConfig() throws Exception { // setUp() fills default values for mcc-mnc - String expectedFqdn1 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; - String expectedFqdn2 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String expectedFqdnFromImsi = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; + String expectedFqdnFromEhplmn = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; - mFakeDns.setAnswer(expectedFqdn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); - mFakeDns.setAnswer(expectedFqdn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromImsi, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); ArrayList testInetAddresses = getValidatedServerListWithDefaultParams(false /*isEmergency*/); @@ -244,46 +248,145 @@ public class EpdgSelectorTest { } private void testPlmnResolutionMethod(boolean isEmergency) throws Exception { - String expectedFqdnFromHplmn = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; - String expectedFqdnFromEHplmn = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; - String expectedFqdnFromConfig = "epdg.epc.mnc480.mcc310.pub.3gppnetwork.org"; + String expectedFqdnFromImsi = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; + String expectedFqdnFromRplmn = "epdg.epc.mnc121.mcc311.pub.3gppnetwork.org"; + String expectedFqdnFromEhplmn = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String excludedFqdnFromConfig = "epdg.epc.mnc480.mcc310.pub.3gppnetwork.org"; + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("311121"); mTestBundle.putIntArray( CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); mTestBundle.putStringArray( CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, - new String[] {"310-480", "300-120", "311-120"}); + new String[] {"310-480", "300-120", "311-120", "311-121"}); - mFakeDns.setAnswer(expectedFqdnFromHplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A); - mFakeDns.setAnswer(expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); - mFakeDns.setAnswer(expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromImsi, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(excludedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer("sos." + expectedFqdnFromImsi, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); mFakeDns.setAnswer( - "sos." + expectedFqdnFromHplmn, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); + "sos." + expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_4}, TYPE_A); mFakeDns.setAnswer( - "sos." + expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_4}, TYPE_A); + "sos." + excludedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_5}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromRplmn, new String[] {TEST_IP_ADDRESS_6}, TYPE_A); mFakeDns.setAnswer( - "sos." + expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_5}, TYPE_A); + "sos." + expectedFqdnFromRplmn, new String[] {TEST_IP_ADDRESS_7}, TYPE_A); ArrayList testInetAddresses = getValidatedServerListWithDefaultParams(isEmergency); if (isEmergency) { assertEquals(6, testInetAddresses.size()); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_3), testInetAddresses.get(0)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_4), testInetAddresses.get(2)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(3)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_5), testInetAddresses.get(4)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(5)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_7), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_6), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_3), testInetAddresses.get(2)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(3)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_4), testInetAddresses.get(4)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(5)); } else { assertEquals(3, testInetAddresses.size()); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(1)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_6), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(2)); } } + @Test + public void testPlmnResolutionMethodWithDuplicatedImsiAndEhplmn() throws Exception { + String fqdnFromEhplmn1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn2AndImsi = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; + String fqdnFromEhplmn3 = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn4 = "epdg.epc.mnc123.mcc300.pub.3gppnetwork.org"; + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300121"); + ehplmnList.add("300122"); + ehplmnList.add("300123"); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] { + CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL, + }); + + mFakeDns.setAnswer(fqdnFromEhplmn1, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn2AndImsi, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn4, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); + + ArrayList testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(4, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_3), testInetAddresses.get(3)); + } + + @Test + public void testPlmnResolutionMethodWithFirstEhplmn() throws Exception { + String fqdnFromEhplmn1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn2 = "epdg.epc.mnc121.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn3 = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn4 = "epdg.epc.mnc123.mcc300.pub.3gppnetwork.org"; + + ehplmnList.add("300121"); + ehplmnList.add("300122"); + ehplmnList.add("300123"); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_FIRST}); + + mFakeDns.setAnswer(fqdnFromEhplmn1, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn2, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn4, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); + + ArrayList testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(1, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); + } + + @Test + public void testPlmnResolutionMethodWithRplmn() throws Exception { + String fqdnFromRplmn = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn2 = "epdg.epc.mnc121.mcc300.pub.3gppnetwork.org"; + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300122"); + ehplmnList.add("300121"); + + mTestBundle.putStringArray( + CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, + new String[] {"310-480", "300-122", "300-121"}); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN}); + + mFakeDns.setAnswer(fqdnFromRplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + + ArrayList testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(1, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); + } + @Test public void testCarrierConfigStaticAddressList() throws Exception { // Set Network.getAllByName mock -- cgit v1.2.3 From 1cc58c15092c2fd3c7d6a95151abec2c5d76f16c Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Mon, 26 Jun 2023 18:11:43 -0700 Subject: Provide an ifname to DataCallResponse In cases where there a data call is successfully established, there should always be an interface name associated with the DataCallResponse. This change ensures that one is always set as part of the tests. Bug: 288898029 Test: atest IwlanDataServiceTest Change-Id: If971401ce2c7c9060d8969f391e0a232066ecc3b --- test/com/google/android/iwlan/IwlanDataServiceTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java index 72ec540..a9b8d80 100644 --- a/test/com/google/android/iwlan/IwlanDataServiceTest.java +++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java @@ -266,6 +266,8 @@ public class IwlanDataServiceTest { when(mMockConnectivityManager.getLinkProperties(eq(mMockNetwork))) .thenReturn(mLinkProperties); + + when(mMockTunnelLinkProperties.ifaceName()).thenReturn("mockipsec0"); } @After -- cgit v1.2.3 From 03740790d77d918d7f7c8ab303483049fb6a9e47 Mon Sep 17 00:00:00 2001 From: Munikrishna Date: Wed, 8 Nov 2023 15:32:17 +0000 Subject: Add AES-GCM algorithms to IWLAN Adding AES-GCM algorithm which is Authenticated Encryption algorithm. This algorithm supports both authentication and Encryption. so, adding authentication algorithm is not required in SA proposal. Bug: b/306119890 Test: atest IwlanTests Change-Id: I75b19837566008acfad508aea85f22fdc4410a7a --- flags/main.aconfig | 8 +- .../android/iwlan/epdg/EpdgTunnelManager.java | 135 ++++++++++++++++++++- .../android/iwlan/epdg/EpdgTunnelManagerTest.java | 62 +++++++++- 3 files changed, 199 insertions(+), 6 deletions(-) diff --git a/flags/main.aconfig b/flags/main.aconfig index a618971..b7ec6fe 100644 --- a/flags/main.aconfig +++ b/flags/main.aconfig @@ -5,4 +5,10 @@ flag { namespace: "iwlan_telephony" description: "Update number of epdg selection threads to prevent thread pool exhausted." bug: "278788983" -} \ No newline at end of file +} +flag { + name: "aead_algos_enabled" + namespace: "iwlan_telephony" + description: "Add AES_GCM algorithm support in IWLAN." + bug: "306119890" +} diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java index b2a5acc..48ef67c 100644 --- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java +++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java @@ -81,6 +81,8 @@ import com.google.android.iwlan.TunnelMetricsInterface; import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics; import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics; import com.google.android.iwlan.exceptions.IwlanSimNotReadyException; +import com.google.android.iwlan.flags.FeatureFlags; +import com.google.android.iwlan.flags.FeatureFlagsImpl; import java.io.IOException; import java.io.PrintWriter; @@ -102,6 +104,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class EpdgTunnelManager { + private final FeatureFlags mFeatureFlags; private final Context mContext; private final int mSlotId; private Handler mHandler; @@ -182,6 +185,7 @@ public class EpdgTunnelManager { private static final Set VALID_PRF_ALGOS; private static final Set VALID_INTEGRITY_ALGOS; private static final Set VALID_ENCRYPTION_ALGOS; + private static final Set VALID_AEAD_ALGOS; private static final String CONFIG_TYPE_DH_GROUP = "dh group"; private static final String CONFIG_TYPE_KEY_LEN = "algorithm key length"; @@ -216,6 +220,12 @@ public class EpdgTunnelManager { SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); + VALID_AEAD_ALGOS = + Set.of( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16); + VALID_PRF_ALGOS = Set.of( SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, @@ -604,9 +614,11 @@ public class EpdgTunnelManager { } } - private EpdgTunnelManager(Context context, int slotId) { + @VisibleForTesting + EpdgTunnelManager(Context context, int slotId, FeatureFlags featureFlags) { mContext = context; mSlotId = slotId; + mFeatureFlags = featureFlags; mIkeSessionCreator = new IkeSessionCreator(); mIpSecManager = mContext.getSystemService(IpSecManager.class); TAG = EpdgTunnelManager.class.getSimpleName() + "[" + mSlotId + "]"; @@ -634,7 +646,7 @@ public class EpdgTunnelManager { */ public static EpdgTunnelManager getInstance(@NonNull Context context, int subId) { return mTunnelManagerInstances.computeIfAbsent( - subId, k -> new EpdgTunnelManager(context, subId)); + subId, k -> new EpdgTunnelManager(context, subId, new FeatureFlagsImpl())); } @VisibleForTesting @@ -870,7 +882,11 @@ public class EpdgTunnelManager { new TunnelModeChildSessionParams.Builder() .setLifetimeSeconds(hardTimeSeconds, softTimeSeconds); - childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal()); + if (mFeatureFlags.aeadAlgosEnabled() && isChildSessionAeadAlgosAvailable()) { + childSessionParamsBuilder.addChildSaProposal(buildAeadChildSaProposal()); + } else { + childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal()); + } boolean handoverIPv4Present = setupRequest.srcIpv4Address().isPresent(); boolean handoverIPv6Present = setupRequest.srcIpv6Address().isPresent(); @@ -999,7 +1015,6 @@ public class EpdgTunnelManager { .setLocalIdentification(getLocalIdentification()) .setRemoteIdentification(getId(setupRequest.apnName(), false)) .setAuthEap(null, getEapConfig()) - .addIkeSaProposal(buildIkeSaProposal()) .setNetwork(mDefaultNetwork) .addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID) .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE) @@ -1008,6 +1023,12 @@ public class EpdgTunnelManager { .setRetransmissionTimeoutsMillis(getRetransmissionTimeoutsFromConfig()) .setDpdDelaySeconds(getDpdDelayFromConfig()); + if (mFeatureFlags.aeadAlgosEnabled() && isIkeSessionAeadAlgosAvailable()) { + builder.addIkeSaProposal(buildIkeSaAeadProposal()); + } else { + builder.addIkeSaProposal(buildIkeSaProposal()); + } + if (numPdnTunnels() == 0) { builder.addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT); Log.d(TAG, "IKE_OPTION_INITIAL_CONTACT"); @@ -1072,6 +1093,32 @@ public class EpdgTunnelManager { builder3gppParams.build(), new TmIke3gppCallback(apnName, token)); } + private boolean isChildSessionAeadAlgosAvailable() { + int[] encryptionAlgos = + getConfig( + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY); + for (int encryptionAlgo : encryptionAlgos) { + if (validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { + return true; + } + } + return false; + } + + private boolean isIkeSessionAeadAlgosAvailable() { + int[] encryptionAlgos = + getConfig( + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY); + for (int encryptionAlgo : encryptionAlgos) { + if (validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { + return true; + } + } + return false; + } + private boolean isValidChildSessionLifetime(int hardLifetimeSeconds, int softLifetimeSeconds) { return hardLifetimeSeconds >= CHILD_HARD_LIFETIME_SEC_MINIMUM && hardLifetimeSeconds <= CHILD_HARD_LIFETIME_SEC_MAXIMUM @@ -1151,6 +1198,50 @@ public class EpdgTunnelManager { return saProposalBuilder.build(); } + private IkeSaProposal buildIkeSaAeadProposal() { + IkeSaProposal.Builder saProposalBuilder = new IkeSaProposal.Builder(); + + int[] dhGroups = getConfig(CarrierConfigManager.Iwlan.KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY); + for (int dhGroup : dhGroups) { + if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) { + saProposalBuilder.addDhGroup(dhGroup); + } + } + + int[] encryptionAlgos = + getConfig( + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY); + for (int encryptionAlgo : encryptionAlgos) { + if (!validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { + continue; + } + if ((encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8) + || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12) + || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16)) { + int[] aesGcmKeyLens = + getConfig( + CarrierConfigManager.Iwlan + .KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY); + for (int aesGcmKeyLen : aesGcmKeyLens) { + if (validateConfig(aesGcmKeyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) { + saProposalBuilder.addEncryptionAlgorithm(encryptionAlgo, aesGcmKeyLen); + } + } + } + } + + int[] prfAlgos = + getConfig(CarrierConfigManager.Iwlan.KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY); + for (int prfAlgo : prfAlgos) { + if (validateConfig(prfAlgo, VALID_PRF_ALGOS, CONFIG_TYPE_PRF_ALGO)) { + saProposalBuilder.addPseudorandomFunction(prfAlgo); + } + } + + return saProposalBuilder.build(); + } + private boolean validateConfig(int config, Set validConfigValues, String configType) { if (validConfigValues.contains(config)) { return true; @@ -1230,6 +1321,42 @@ public class EpdgTunnelManager { return saProposalBuilder.build(); } + private ChildSaProposal buildAeadChildSaProposal() { + ChildSaProposal.Builder saProposalBuilder = new ChildSaProposal.Builder(); + + int[] dhGroups = getConfig(CarrierConfigManager.Iwlan.KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY); + for (int dhGroup : dhGroups) { + if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) { + saProposalBuilder.addDhGroup(dhGroup); + } + } + + int[] encryptionAlgos = + getConfig( + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY); + for (int encryptionAlgo : encryptionAlgos) { + if (!validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { + continue; + } + if ((encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8) + || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12) + || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16)) { + int[] aesGcmKeyLens = + getConfig( + CarrierConfigManager.Iwlan + .KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY); + for (int aesGcmKeyLen : aesGcmKeyLens) { + if (validateConfig(aesGcmKeyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) { + saProposalBuilder.addEncryptionAlgorithm(encryptionAlgo, aesGcmKeyLen); + } + } + } + } + + return saProposalBuilder.build(); + } + private IkeIdentification getLocalIdentification() throws IwlanSimNotReadyException { String nai; diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java index 64ad2c2..0326c44 100644 --- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java @@ -72,11 +72,13 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; +import android.util.Pair; import com.google.android.iwlan.IwlanError; import com.google.android.iwlan.IwlanTunnelMetricsImpl; import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics; import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics; +import com.google.android.iwlan.flags.FeatureFlags; import org.junit.Before; import org.junit.Ignore; @@ -140,6 +142,7 @@ public class EpdgTunnelManagerTest { @Mock private IwlanTunnelMetricsImpl mMockIwlanTunnelMetrics; @Mock private IkeSession mMockIkeSession; @Mock private EpdgSelector mMockEpdgSelector; + @Mock private FeatureFlags mFakeFeatureFlags; @Mock CarrierConfigManager mMockCarrierConfigManager; @Mock ConnectivityManager mMockConnectivityManager; @Mock SubscriptionManager mMockSubscriptionManager; @@ -173,7 +176,8 @@ public class EpdgTunnelManagerTest { EpdgTunnelManager.resetAllInstances(); when(mMockContext.getSystemService(eq(IpSecManager.class))).thenReturn(mMockIpSecManager); - mEpdgTunnelManager = spy(EpdgTunnelManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX)); + mEpdgTunnelManager = + spy(new EpdgTunnelManager(mMockContext, DEFAULT_SLOT_INDEX, mFakeFeatureFlags)); doReturn(mTestLooper.getLooper()).when(mEpdgTunnelManager).getLooper(); setVariable(mEpdgTunnelManager, "mContext", mMockContext); mEpdgTunnelManager.initHandler(); @@ -538,6 +542,62 @@ public class EpdgTunnelManagerTest { assertFalse(secondTunnelParams.hasIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT)); } + @Test + public void testAeadSaProposals() throws Exception { + when(mFakeFeatureFlags.aeadAlgosEnabled()).thenReturn(true); + final String apnName = "ims"; + int[] aeadAlgos = { + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, + }; + int[] aeadAlgosKeyLens = { + SaProposal.KEY_LEN_AES_128, SaProposal.KEY_LEN_AES_192, SaProposal.KEY_LEN_AES_256, + }; + + PersistableBundle bundle = new PersistableBundle(); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY, + aeadAlgos); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, + aeadAlgosKeyLens); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY, + aeadAlgos); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, + aeadAlgosKeyLens); + + setupMockForGetConfig(bundle); + + IkeSessionArgumentCaptors tunnelArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockDefaultNetwork); + + IkeSessionParams ikeTunnelParams = tunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue(); + + List> ikeEncrAlgos = + ikeTunnelParams.getIkeSaProposals().get(0).getEncryptionAlgorithms(); + + assertTrue(ikeEncrAlgos.contains(new Pair(aeadAlgos[0], aeadAlgosKeyLens[0]))); + assertEquals( + "IKE AEAD algorithms mismatch", + (long) aeadAlgos.length * aeadAlgosKeyLens.length, + (long) ikeEncrAlgos.size()); + + ChildSessionParams childTunnelParams = + tunnelArgumentCaptors.mChildSessionParamsCaptor.getValue(); + + List> childEncrAlgos = + childTunnelParams.getChildSaProposals().get(0).getEncryptionAlgorithms(); + + assertTrue(childEncrAlgos.contains(new Pair(aeadAlgos[0], aeadAlgosKeyLens[0]))); + assertEquals( + "Child AEAD algorithms mismatch", + (long) aeadAlgos.length * aeadAlgosKeyLens.length, + (long) childEncrAlgos.size()); + } + @Test public void testCloseTunnelWithNoTunnelForApn() throws Exception { String testApnName = "www.xyz.com"; -- cgit v1.2.3 From 58ced554f404cba1fc08d33fa3123106a8ab5e12 Mon Sep 17 00:00:00 2001 From: Po-Chun Lee Date: Thu, 23 Nov 2023 06:33:35 +0000 Subject: Add IwlanCarrierConfig for Default Non-AOSP Carrier Configurations Implemented IwlanCarrierConfig to provide default values for non-AOSP carrier configurations. This utility serves as a temporary solution to test new carrier configurations before their integration into AOSP, replacing the use of IwlanHelper#getConfig(). Notably, IwlanHelper#getConfig() relies on deprecated APIs: * CarrierConfigManager#getConfigForSubId(int sub_id) The newly introduced utilities avoid these deprecated methods. Test: atest Test: on-device function test Bug: 313390507 Bug: 296608664 (cherry picked from https://android-review.googlesource.com/q/commit:23614aa3cc580183a5e608469d61106d7865dfd2) Merged-In: Ie2ed17ad967b488b2de3d8cb43ab2f967929b22a Change-Id: Ie2ed17ad967b488b2de3d8cb43ab2f967929b22a --- .../google/android/iwlan/IwlanCarrierConfig.java | 112 ++++++++++++++++++ src/com/google/android/iwlan/IwlanHelper.java | 9 ++ .../android/iwlan/IwlanCarrierConfigTest.java | 127 +++++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 src/com/google/android/iwlan/IwlanCarrierConfig.java create mode 100644 test/com/google/android/iwlan/IwlanCarrierConfigTest.java diff --git a/src/com/google/android/iwlan/IwlanCarrierConfig.java b/src/com/google/android/iwlan/IwlanCarrierConfig.java new file mode 100644 index 0000000..ab5ac7b --- /dev/null +++ b/src/com/google/android/iwlan/IwlanCarrierConfig.java @@ -0,0 +1,112 @@ +/* + * Copyright 2023 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.google.android.iwlan; + +import android.content.Context; +import android.os.PersistableBundle; +import android.support.annotation.NonNull; +import android.telephony.CarrierConfigManager; + +/** Class for handling IWLAN carrier configuration. */ +public class IwlanCarrierConfig { + + /** + * Key for setting the delay in seconds to release the IWLAN connection after a handover to + * WWAN. Refer to {@link #DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT} for the default + * value. + */ + public static final String KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT = + "iwlan.handover_to_wwan_release_delay_second_int"; + + /** + * Default delay in seconds for releasing the IWLAN connection after a WWAN handover. This is + * the default value for {@link #KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT}. + */ + public static final int DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT = 0; + + private static PersistableBundle mHiddenBundle = new PersistableBundle(); + + static { + mHiddenBundle = createHiddenDefaultConfig(); + } + + /** + * Creates a hidden default configuration. + * + * @return a PersistableBundle containing the hidden default configuration + */ + private static @NonNull PersistableBundle createHiddenDefaultConfig() { + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt( + KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT, + DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT); + return bundle; + } + + /** + * Gets a configuration value for a given slot ID and key. + * + * @param context the application context + * @param slotId the slot ID + * @param key the configuration key + * @return the configuration value + */ + // TODO: b/313425985 - Refactor to support explicit type handling (e.g., getIntConfig, + // getStringConfig). This will enhance type safety by eliminating the need for unchecked casting + @SuppressWarnings("TypeParameterUnusedInFormals") + public static T getConfig(Context context, int slotId, String key) { + CarrierConfigManager carrierConfigManager = + context.getSystemService(CarrierConfigManager.class); + if (carrierConfigManager == null) { + return getDefaultConfig(key); + } + + int subId = IwlanHelper.getSubId(context, slotId); + PersistableBundle bundle = carrierConfigManager.getConfigForSubId(subId, key); + if (!bundle.containsKey(key)) { + return getDefaultConfig(key); + } + + return (T) bundle.get(key); + } + + /** + * Gets the default configuration value for a given key. + * + *

TODO(b/313425985): Refactor to support explicit type handling. This will enhance type + * safety by eliminating the need for unchecked casting + * + * @param key the configuration key + * @return the default configuration value + * @throws IllegalArgumentException if the default configuration is null for the given key + */ + // TODO: b/313425985 - Refactor to support explicit type handling. This will enhance type + // safety by eliminating the need for unchecked casting + @SuppressWarnings("TypeParameterUnusedInFormals") + public static T getDefaultConfig(String key) { + PersistableBundle bundle = CarrierConfigManager.getDefaultConfig(); + if (bundle.containsKey(key)) { + return (T) bundle.get(key); + } + + if (mHiddenBundle.containsKey(key)) { + return (T) mHiddenBundle.get(key); + } + + throw new IllegalArgumentException("Default config not found for key: " + key); + } +} diff --git a/src/com/google/android/iwlan/IwlanHelper.java b/src/com/google/android/iwlan/IwlanHelper.java index ade7189..183a967 100644 --- a/src/com/google/android/iwlan/IwlanHelper.java +++ b/src/com/google/android/iwlan/IwlanHelper.java @@ -185,6 +185,10 @@ public class IwlanHelper { throw new IllegalStateException("Local address should not be null."); } + /** + * @deprecated This method is deprecated. Use {@link IwlanCarrierConfig#getConfig()} instead. + */ + @Deprecated public static T getConfig(String key, Context context, int slotId) { CarrierConfigManager carrierConfigManager = context.getSystemService(CarrierConfigManager.class); @@ -202,6 +206,11 @@ public class IwlanHelper { } } + /** + * @deprecated This method is deprecated. Use {@link IwlanCarrierConfig#getDefaultConfig()} + * instead. + */ + @Deprecated public static T getDefaultConfig(String key) { PersistableBundle bundle = CarrierConfigManager.getDefaultConfig(); if (bundle == null) { diff --git a/test/com/google/android/iwlan/IwlanCarrierConfigTest.java b/test/com/google/android/iwlan/IwlanCarrierConfigTest.java new file mode 100644 index 0000000..0421300 --- /dev/null +++ b/test/com/google/android/iwlan/IwlanCarrierConfigTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2023 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.google.android.iwlan; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; + +public class IwlanCarrierConfigTest { + private static final int DEFAULT_SUB_ID = 0; + private static final int DEFAULT_SLOT_ID = 1; + + private static final String KEY_CONFIG_IN_SUB = "iwlan.key_config_in_sub"; + private static final String KEY_CONFIG_IN_DEFAULT = "iwlan.key_config_in_default"; + + private static final int VALUE_CONFIG_IN_SUB = 10; + private static final int VALUE_CONFIG_IN_DEFAULT = 20; + + @Mock private Context mMockContext; + @Mock private CarrierConfigManager mMockCarrierConfigManager; + @Mock private SubscriptionManager mMockSubscriptionManager; + @Mock private SubscriptionInfo mMockSubscriptionInfo; + + private PersistableBundle mBundleForSub; + private PersistableBundle mBundleForDefault; + + MockitoSession mStaticMockSession; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mStaticMockSession = + mockitoSession() + .mockStatic(SubscriptionManager.class) + .mockStatic(CarrierConfigManager.class) + .startMocking(); + + when(mMockContext.getSystemService(CarrierConfigManager.class)) + .thenReturn(mMockCarrierConfigManager); + when(mMockContext.getSystemService(SubscriptionManager.class)) + .thenReturn(mMockSubscriptionManager); + + when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt())) + .thenReturn(mMockSubscriptionInfo); + when(mMockSubscriptionInfo.getSubscriptionId()).thenReturn(DEFAULT_SUB_ID); + + mBundleForSub = new PersistableBundle(); + mBundleForSub.putInt(KEY_CONFIG_IN_SUB, VALUE_CONFIG_IN_SUB); + lenient() + .when(mMockCarrierConfigManager.getConfigForSubId(anyInt(), anyString())) + .thenReturn(mBundleForSub); + + mBundleForDefault = new PersistableBundle(); + mBundleForDefault.putInt(KEY_CONFIG_IN_DEFAULT, VALUE_CONFIG_IN_DEFAULT); + lenient().when(CarrierConfigManager.getDefaultConfig()).thenReturn(mBundleForDefault); + } + + @After + public void cleanUp() throws Exception { + mStaticMockSession.finishMocking(); + } + + @Test + public void testGetConfig_ValidRetrieval() { + int result = IwlanCarrierConfig.getConfig(mMockContext, DEFAULT_SLOT_ID, KEY_CONFIG_IN_SUB); + assertEquals(VALUE_CONFIG_IN_SUB, result); + } + + @Test + public void testGetConfig_KeyNotFound() { + // Default value from getDefaultConfig + int result = + IwlanCarrierConfig.getConfig(mMockContext, DEFAULT_SLOT_ID, KEY_CONFIG_IN_DEFAULT); + assertEquals(VALUE_CONFIG_IN_DEFAULT, result); + } + + @Test + public void testGetDefaultConfig_KeyFound() { + int result = IwlanCarrierConfig.getDefaultConfig(KEY_CONFIG_IN_DEFAULT); + assertEquals(VALUE_CONFIG_IN_DEFAULT, result); + } + + @Test + public void testGetDefaultConfig_KeyInHiddenDefault() { + int result = + IwlanCarrierConfig.getDefaultConfig( + IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT); + assertEquals(IwlanCarrierConfig.DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT, result); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetDefaultConfig_KeyNotFound() { + IwlanCarrierConfig.getDefaultConfig("non_existing_key"); + } +} -- cgit v1.2.3 From 3059be27ca0d5ea3794d389c0853983265f8c440 Mon Sep 17 00:00:00 2001 From: Po-Chun Lee Date: Thu, 23 Nov 2023 06:33:51 +0000 Subject: Add a delay in deactivateDataCall for handovers To address issues during handovers, a delay is added in handling deactivateDataCall after handover to WWAN. This change ensures that the network has time to release the tunnel. If not released within 3 seconds, Iwlan will forcefully terminate the session using IkeSession#kill(). Bug: 312074917 Bug: 296608664 Test: atest(new) Test: on-device regression (cherry picked from https://android-review.googlesource.com/q/commit:f69a5f90aaf38ac160198e1dfe256e8df42f666a) Merged-In: If85b2f5ba345e128a539960c116149a8290c66f0 Change-Id: If85b2f5ba345e128a539960c116149a8290c66f0 --- src/com/google/android/iwlan/IwlanDataService.java | 208 +++++++++++++++------ .../google/android/iwlan/IwlanDataServiceTest.java | 138 +++++++++++++- 2 files changed, 287 insertions(+), 59 deletions(-) diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java index d4c7dce..3c37421 100644 --- a/src/com/google/android/iwlan/IwlanDataService.java +++ b/src/com/google/android/iwlan/IwlanDataService.java @@ -115,6 +115,7 @@ public class IwlanDataService extends DataService { private static final int EVENT_REMOVE_DATA_SERVICE_PROVIDER = EVENT_BASE + 7; private static final int EVENT_TUNNEL_OPENED_METRICS = EVENT_BASE + 8; private static final int EVENT_TUNNEL_CLOSED_METRICS = EVENT_BASE + 9; + private static final int EVENT_DEACTIVATE_DATA_CALL_WITH_DELAY = EVENT_BASE + 10; @VisibleForTesting enum Transport { @@ -210,10 +211,10 @@ public class IwlanDataService extends DataService { @VisibleForTesting class IwlanDataServiceProvider extends DataService.DataServiceProvider { - private static final int CALLBACK_TYPE_SETUP_DATACALL_COMPLETE = 1; private static final int CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE = 2; private static final int CALLBACK_TYPE_GET_DATACALL_LIST_COMPLETE = 3; + private final String SUB_TAG; private final IwlanDataService mIwlanDataService; private final IwlanTunnelCallback mIwlanTunnelCallback; @@ -254,6 +255,7 @@ public class IwlanDataService extends DataService { private Date mBringUpStateTime = null; private Date mUpStateTime = null; private boolean mIsImsOrEmergency; + private DeactivateDataCallData mPendingDeactivateDataCallData; public int getPduSessionId() { return mPduSessionId; @@ -306,6 +308,14 @@ public class IwlanDataService extends DataService { return mState; } + public DeactivateDataCallData getPendingDeactivateDataCallData() { + return mPendingDeactivateDataCallData; + } + + public boolean hasPendingDeactivateDataCallData() { + return mPendingDeactivateDataCallData != null; + } + /** * @param state (TunnelState.TUNNEL_DOWN|TUNNEL_UP|TUNNEL_DOWN) */ @@ -347,6 +357,11 @@ public class IwlanDataService extends DataService { mIsImsOrEmergency = isImsOrEmergency; } + public void setPendingDeactivateDataCallData( + DeactivateDataCallData deactivateDataCallData) { + mPendingDeactivateDataCallData = deactivateDataCallData; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -827,6 +842,11 @@ public class IwlanDataService extends DataService { * support data connection tear down. When completed or error, the service must invoke the * provided callback to notify the platform. * + *

Note: For handovers, in compliance with 3GPP specs (TS 23.402 clause 8.6.1, TS 23.502 + * clause 4.11.4.1), a {@link KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT} delay is + * implemented to allow the network to release the IKE tunnel. If the network fails to + * release it within this timeframe, the UE will take over the release process. + * * @param cid Call id returned in the callback of {@link * DataServiceProvider#setupDataCall(int, DataProfile, boolean, boolean, int, * LinkProperties, DataServiceCallback)}. @@ -846,14 +866,28 @@ public class IwlanDataService extends DataService { + "callback: " + callback); + boolean isRequestForHandoverToWWAN = (reason == REQUEST_REASON_HANDOVER); + + int delayTimeSeconds = 0; + if (isRequestForHandoverToWWAN) { + delayTimeSeconds = + IwlanCarrierConfig.getConfig( + mContext, + getSlotIndex(), + IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT); + } + + int event = + (delayTimeSeconds > 0) + ? EVENT_DEACTIVATE_DATA_CALL_WITH_DELAY + : EVENT_DEACTIVATE_DATA_CALL; + DeactivateDataCallData deactivateDataCallData = - new DeactivateDataCallData(cid, reason, callback, this); + new DeactivateDataCallData(cid, reason, callback, this, delayTimeSeconds); getIwlanDataServiceHandler() - .sendMessage( - getIwlanDataServiceHandler() - .obtainMessage( - EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData)); + .obtainMessage(event, deactivateDataCallData) + .sendToTarget(); } public void forceCloseTunnelsInDeactivatingState() { @@ -1203,6 +1237,15 @@ public class IwlanDataService extends DataService { break; } + if (tunnelState.hasPendingDeactivateDataCallData()) { + // Iwlan delays handling EVENT_DEACTIVATE_DATA_CALL to give the network time + // to release the PDN. This allows for immediate response to Telephony if + // the network releases the PDN before timeout. Otherwise, Telephony's PDN + // state waits for Iwlan, blocking further actions on this PDN. + resumePendingDeactivationIfExists( + tunnelState.getPendingDeactivateDataCallData()); + } + iwlanDataServiceProvider.mTunnelStats.reportTunnelDown(apnName, tunnelState); iwlanDataServiceProvider.mTunnelStateForApn.remove(apnName); metricsAtom = iwlanDataServiceProvider.mMetricsAtomForApn.get(apnName); @@ -1556,55 +1599,11 @@ public class IwlanDataService extends DataService { break; case EVENT_DEACTIVATE_DATA_CALL: - DeactivateDataCallData deactivateDataCallData = - (DeactivateDataCallData) msg.obj; - iwlanDataServiceProvider = deactivateDataCallData.mIwlanDataServiceProvider; - callback = deactivateDataCallData.mCallback; - reason = deactivateDataCallData.mReason; - - int cid = deactivateDataCallData.mCid; - slotId = iwlanDataServiceProvider.getSlotIndex(); - boolean isNetworkLost = - !isNetworkConnected( - isActiveDataOnOtherSub(slotId), - IwlanHelper.isCrossSimCallingEnabled(mContext, slotId)); - boolean isHandOutSuccessful = (reason == REQUEST_REASON_HANDOVER); - - for (String apn : iwlanDataServiceProvider.mTunnelStateForApn.keySet()) { - if (apn.hashCode() == cid) { - // No need to check state since dataconnection in framework serializes - // setup and deactivate calls using callId/cid. - iwlanDataServiceProvider - .mTunnelStateForApn - .get(apn) - .setState( - IwlanDataServiceProvider.TunnelState - .TUNNEL_IN_BRINGDOWN); - iwlanDataServiceProvider - .mTunnelStateForApn - .get(apn) - .setDataServiceCallback(callback); - - // According to the handover procedure in 3GPP specifications (TS 23.402 - // clause 8.6.1 for S1; TS 23.502 clause 4.11.4.1 for N1), if the PDN is - // handed out to another RAT, the IKE tunnel over ePDG SHOULD be - // released by the network. Thus, UE just released the tunnel locally. - iwlanDataServiceProvider - .getTunnelManager() - .closeTunnel( - apn, - isNetworkLost || isHandOutSuccessful /* forceClose */, - iwlanDataServiceProvider.getIwlanTunnelCallback(), - iwlanDataServiceProvider.getIwlanTunnelMetrics()); - return; - } - } + handleDeactivateDataCall((DeactivateDataCallData) msg.obj); + break; - iwlanDataServiceProvider.deliverCallback( - IwlanDataServiceProvider.CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE, - DataServiceCallback.RESULT_ERROR_INVALID_ARG, - callback, - null); + case EVENT_DEACTIVATE_DATA_CALL_WITH_DELAY: + handleDeactivateDataCallWithDelay((DeactivateDataCallData) msg.obj); break; case EVENT_DATA_CALL_LIST_REQUEST: @@ -1705,6 +1704,97 @@ public class IwlanDataService extends DataService { } } + public void handleDeactivateDataCall(DeactivateDataCallData data) { + handleDeactivateDataCall(data, false); + } + + public void handleDeactivateDataCallWithDelay(DeactivateDataCallData data) { + handleDeactivateDataCall(data, true); + } + + public void handleDeactivateDataCall(DeactivateDataCallData data, boolean isWithDelay) { + IwlanDataServiceProvider serviceProvider = data.mIwlanDataServiceProvider; + String matchingApn = findMatchingApn(serviceProvider, data.mCid); + + if (matchingApn == null) { + deliverDeactivationError(serviceProvider, data.mCallback); + return; + } + + if (isWithDelay) { + Log.d(TAG, "Delaying deactivation for APN: " + matchingApn); + scheduleDelayedDeactivateDataCall(serviceProvider, data, matchingApn); + return; + } + Log.d(TAG, "Processing deactivation for APN: " + matchingApn); + processDeactivateDataCall(serviceProvider, data, matchingApn); + } + + private String findMatchingApn(IwlanDataServiceProvider serviceProvider, int cid) { + return serviceProvider.mTunnelStateForApn.keySet().stream() + .filter(apn -> apn.hashCode() == cid) + .findFirst() + .orElse(null); + } + + private void deliverDeactivationError( + IwlanDataServiceProvider serviceProvider, DataServiceCallback callback) { + serviceProvider.deliverCallback( + IwlanDataServiceProvider.CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE, + DataServiceCallback.RESULT_ERROR_INVALID_ARG, + callback, + null); + } + + private void scheduleDelayedDeactivateDataCall( + IwlanDataServiceProvider serviceProvider, + DeactivateDataCallData data, + String matchingApn) { + IwlanDataServiceProvider.TunnelState tunnelState = + serviceProvider.mTunnelStateForApn.get(matchingApn); + tunnelState.setPendingDeactivateDataCallData(data); + Handler handler = getIwlanDataServiceHandler(); + handler.sendMessageDelayed( + handler.obtainMessage(EVENT_DEACTIVATE_DATA_CALL, data), + data.mDelayTimeSeconds * 1000L); + } + + private void processDeactivateDataCall( + IwlanDataServiceProvider serviceProvider, + DeactivateDataCallData data, + String matchingApn) { + int slotId = serviceProvider.getSlotIndex(); + boolean isNetworkLost = + !isNetworkConnected( + isActiveDataOnOtherSub(slotId), + IwlanHelper.isCrossSimCallingEnabled(mContext, slotId)); + boolean isHandoverSuccessful = (data.mReason == REQUEST_REASON_HANDOVER); + + IwlanDataServiceProvider.TunnelState tunnelState = + serviceProvider.mTunnelStateForApn.get(matchingApn); + tunnelState.setState(IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGDOWN); + tunnelState.setDataServiceCallback(data.mCallback); + + serviceProvider + .getTunnelManager() + .closeTunnel( + matchingApn, + isNetworkLost || isHandoverSuccessful, /* forceClose */ + serviceProvider.getIwlanTunnelCallback(), + serviceProvider.getIwlanTunnelMetrics()); + } + + private void resumePendingDeactivationIfExists( + DeactivateDataCallData deactivateDataCallData) { + Handler handler = getIwlanDataServiceHandler(); + if (handler.hasMessages(EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData)) { + // Remove any existing deactivation messages and request a new one in the front + handler.removeMessages(EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData); + handler.sendMessageAtFrontOfQueue( + handler.obtainMessage(EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData)); + } + } + IwlanDataServiceHandler(Looper looper) { super(looper); } @@ -1788,13 +1878,19 @@ public class IwlanDataService extends DataService { final int mReason; final DataServiceCallback mCallback; final IwlanDataServiceProvider mIwlanDataServiceProvider; + final int mDelayTimeSeconds; private DeactivateDataCallData( - int cid, int reason, DataServiceCallback callback, IwlanDataServiceProvider dsp) { + int cid, + int reason, + DataServiceCallback callback, + IwlanDataServiceProvider dsp, + int delayTimeSeconds) { mCid = cid; mReason = reason; mCallback = callback; mIwlanDataServiceProvider = dsp; + mDelayTimeSeconds = delayTimeSeconds; } } @@ -2062,6 +2158,8 @@ public class IwlanDataService extends DataService { return "EVENT_TUNNEL_OPENED_METRICS"; case EVENT_TUNNEL_CLOSED_METRICS: return "EVENT_TUNNEL_CLOSED_METRICS"; + case EVENT_DEACTIVATE_DATA_CALL_WITH_DELAY: + return "EVENT_DEACTIVATE_DATA_CALL_WITH_DELAY"; case IwlanEventListener.CALL_STATE_CHANGED_EVENT: return "CALL_STATE_CHANGED_EVENT"; default: diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java index b7cda5b..6f2ee6c 100644 --- a/test/com/google/android/iwlan/IwlanDataServiceTest.java +++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java @@ -205,12 +205,12 @@ public class IwlanDataServiceTest { .mockStatic(ErrorPolicyManager.class) .mockStatic(IwlanBroadcastReceiver.class) .mockStatic(SubscriptionManager.class) + .spyStatic(IwlanCarrierConfig.class) .strictness(Strictness.LENIENT) .startMocking(); when(mMockContext.getSystemService(eq(ConnectivityManager.class))) .thenReturn(mMockConnectivityManager); - when(mMockContext.getSystemService(eq(SubscriptionManager.class))) .thenReturn(mMockSubscriptionManager); @@ -272,6 +272,11 @@ public class IwlanDataServiceTest { .thenReturn(mLinkProperties); } + private void moveTimeForwardAndDispatch(long milliSeconds) { + mTestLooper.moveTimeForward(milliSeconds); + mTestLooper.dispatchAll(); + } + @After public void cleanUp() throws Exception { mStaticMockSession.finishMocking(); @@ -836,7 +841,7 @@ public class IwlanDataServiceTest { } @Test - public void testIwlanDeactivateDataCallAfterSuccessHandover() { + public void testDeactivateDataCall_ImmediateReleaseAfterHandover() { DataProfile dp = buildImsDataProfile(); onSystemDefaultNetworkConnected(TRANSPORT_WIFI); @@ -844,7 +849,7 @@ public class IwlanDataServiceTest { mSpyIwlanDataServiceProvider.setTunnelState( dp, mMockDataServiceCallback, - TunnelState.TUNNEL_IN_BRINGUP, + TunnelState.TUNNEL_UP, null, /* linkProperties */ false, /* isHandover */ 1, /* pduSessionId */ @@ -855,11 +860,13 @@ public class IwlanDataServiceTest { DataService.REQUEST_REASON_HANDOVER, mMockDataServiceCallback); mTestLooper.dispatchAll(); + + moveTimeForwardAndDispatch(50); /* Check closeTunnel() is called. */ verify(mMockEpdgTunnelManager, times(1)) .closeTunnel( eq(TEST_APN_NAME), - eq(true), + eq(true) /* forceClose */, any(IwlanTunnelCallback.class), any(IwlanTunnelMetricsImpl.class)); @@ -872,6 +879,129 @@ public class IwlanDataServiceTest { .onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS)); } + @Test + public void testDeactivateDataCall_DelayedReleaseAfterHandover() { + DataProfile dp = buildImsDataProfile(); + + when(IwlanCarrierConfig.getConfig( + mMockContext, + DEFAULT_SLOT_INDEX, + IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT)) + .thenReturn(3); + onSystemDefaultNetworkConnected(TRANSPORT_WIFI); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_UP, + null, /* linkProperties */ + false, /* isHandover */ + 1, /* pduSessionId */ + true /* isImsOrEmergency */); + + mSpyIwlanDataServiceProvider.deactivateDataCall( + TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */, + DataService.REQUEST_REASON_HANDOVER, + mMockDataServiceCallback); + mTestLooper.dispatchAll(); + + moveTimeForwardAndDispatch(2950); + /* Check closeTunnel() is called. */ + verify(mMockEpdgTunnelManager, never()) + .closeTunnel( + eq(TEST_APN_NAME), + anyBoolean(), + any(IwlanTunnelCallback.class), + any(IwlanTunnelMetricsImpl.class)); + + moveTimeForwardAndDispatch(50); + /* Check closeTunnel() is called. */ + verify(mMockEpdgTunnelManager, times(1)) + .closeTunnel( + eq(TEST_APN_NAME), + eq(true) /* forceClose */, + any(IwlanTunnelCallback.class), + any(IwlanTunnelMetricsImpl.class)); + + /* Check callback result is RESULT_SUCCESS when onClosed() is called. */ + mSpyIwlanDataServiceProvider + .getIwlanTunnelCallback() + .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR)); + mTestLooper.dispatchAll(); + verify(mMockDataServiceCallback, times(1)) + .onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS)); + } + + @Test + public void testDeactivateDataCall_DelayedReleaseAfterHandover_NetworkReleaseBeforeDelay() { + DataProfile dp = buildImsDataProfile(); + + when(IwlanCarrierConfig.getConfig( + mMockContext, + DEFAULT_SLOT_INDEX, + IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT)) + .thenReturn(3); + when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX))) + .thenReturn(mMockErrorPolicyManager); + when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME))) + .thenReturn(DataFailCause.NONE); + + onSystemDefaultNetworkConnected(TRANSPORT_WIFI); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_UP, + null, /* linkProperties */ + false, /* isHandover */ + 1, /* pduSessionId */ + true /* isImsOrEmergency */); + + mSpyIwlanDataServiceProvider.setMetricsAtom( + TEST_APN_NAME, + 64, /* type IMS */ + true, + 13, /* LTE */ + false, + true, + 1 /* Transport WiFi */); + + mSpyIwlanDataServiceProvider.deactivateDataCall( + TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */, + DataService.REQUEST_REASON_HANDOVER, + mMockDataServiceCallback); + mTestLooper.dispatchAll(); + + moveTimeForwardAndDispatch(50); + /* Check closeTunnel() is called. */ + verify(mMockEpdgTunnelManager, never()) + .closeTunnel( + eq(TEST_APN_NAME), + anyBoolean(), + any(IwlanTunnelCallback.class), + any(IwlanTunnelMetricsImpl.class)); + + /* Check callback result is RESULT_SUCCESS when onClosed() is called. */ + mSpyIwlanDataServiceProvider + .getIwlanTunnelCallback() + .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR)); + mTestLooper.dispatchAll(); + verify(mMockDataServiceCallback, times(1)) + .onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_ERROR_INVALID_ARG)); + + moveTimeForwardAndDispatch(4000); + + verify(mMockEpdgTunnelManager, never()) + .closeTunnel( + eq(TEST_APN_NAME), + anyBoolean(), + any(IwlanTunnelCallback.class), + any(IwlanTunnelMetricsImpl.class)); + + // No additional callbacks are involved. + verify(mMockDataServiceCallback, times(1)).onDeactivateDataCallComplete(anyInt()); + } + @Test public void testHandoverFailureModeDefault() { DataProfile dp = buildImsDataProfile(); -- cgit v1.2.3 From cd4816384b7d65c4c255deee93bd70985ac995ed Mon Sep 17 00:00:00 2001 From: Munikrishna Date: Fri, 15 Sep 2023 15:06:19 +0000 Subject: ePDG Server address decoding from PCO data PCO data contains MCC, MNC and IP address. Existing implementation is not considering MCC and MNC. So, decoding of IP address is getting wrong data. New implementation considers MCC and MNC data of size 3 bytes to skip to fetch the IP address information from the PCO data. Bug: b/293558904 Test: atest IwlanTests (cherry picked from https://android-review.googlesource.com/q/commit:541210dca89438b5bec138d48ab8e950f3af4bee) Change-Id: I9a291969589a2719d8dd294dc322c84b46098225 --- .../android/iwlan/IwlanBroadcastReceiver.java | 5 ++ .../google/android/iwlan/epdg/EpdgSelector.java | 61 +++++++++++++++------- .../android/iwlan/IwlanBroadcastReceiverTest.java | 22 ++++++++ .../android/iwlan/epdg/EpdgSelectorTest.java | 49 ++++++++++++++--- 4 files changed, 113 insertions(+), 24 deletions(-) diff --git a/src/com/google/android/iwlan/IwlanBroadcastReceiver.java b/src/com/google/android/iwlan/IwlanBroadcastReceiver.java index e6195ba..44f8c24 100644 --- a/src/com/google/android/iwlan/IwlanBroadcastReceiver.java +++ b/src/com/google/android/iwlan/IwlanBroadcastReceiver.java @@ -98,6 +98,11 @@ public class IwlanBroadcastReceiver extends BroadcastReceiver { int pcoId = intent.getIntExtra(TelephonyManager.EXTRA_PCO_ID, 0); byte[] pcoData = intent.getByteArrayExtra(TelephonyManager.EXTRA_PCO_VALUE); + if (pcoData == null) { + Log.e(TAG, "Pco data unavailable"); + return; + } + Log.d( TAG, "PcoID:" diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java index 4534b81..f8fcb4a 100644 --- a/src/com/google/android/iwlan/epdg/EpdgSelector.java +++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java @@ -76,8 +76,8 @@ public class EpdgSelector { new ConcurrentHashMap<>(); private int mV4PcoId = -1; private int mV6PcoId = -1; - private byte[] mV4PcoData = null; - private byte[] mV6PcoData = null; + private List mV4PcoData; + private List mV6PcoData; @NonNull private final ErrorPolicyManager mErrorPolicyManager; // The default DNS timeout in the DNS module is set to 5 seconds. To account for IPC overhead, @@ -89,6 +89,11 @@ public class EpdgSelector { private static final int NUM_EPDG_SELECTION_EXECUTORS = 2; // 1 each for normal selection, SOS. private static final int MAX_EPDG_SELECTION_THREADS = 2; // 1 each for prefetch, tunnel bringup. private static final int MAX_DNS_RESOLVER_THREADS = 25; // Do not expect > 25 FQDNs per carrier. + + private static final int PCO_MCC_MNC_LEN = 3; // 3 bytes for MCC and MNC in PCO data. + private static final int PCO_IPV4_LEN = 4; // 4 bytes for IPv4 address in PCO data. + private static final int PCO_IPV6_LEN = 16; // 16 bytes for IPv6 address in PCO data. + private static final String NO_DOMAIN = "NO_DOMAIN"; BlockingQueue dnsResolutionQueue = @@ -156,6 +161,9 @@ public class EpdgSelector { mContext = context; mSlotId = slotId; + mV4PcoData = new ArrayList<>(); + mV6PcoData = new ArrayList<>(); + mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId); } @@ -164,7 +172,7 @@ public class EpdgSelector { return mSelectorInstances.get(slotId); } - public boolean setPcoData(int pcoId, byte[] pcoData) { + public boolean setPcoData(int pcoId, @NonNull byte[] pcoData) { Log.d( TAG, "onReceive PcoId:" @@ -188,11 +196,11 @@ public class EpdgSelector { if (pcoId == PCO_ID_IPV4) { mV4PcoId = pcoId; - mV4PcoData = pcoData; + mV4PcoData.add(pcoData); return true; } else if (pcoId == PCO_ID_IPV6) { mV6PcoId = pcoId; - mV6PcoData = pcoData; + mV6PcoData.add(pcoData); return true; } @@ -203,8 +211,8 @@ public class EpdgSelector { Log.d(TAG, "Clear PCO data"); mV4PcoId = -1; mV6PcoId = -1; - mV4PcoData = null; - mV6PcoData = null; + mV4PcoData.clear(); + mV6PcoData.clear(); } private CompletableFuture>> submitDnsResolverQuery( @@ -857,7 +865,7 @@ public class EpdgSelector { } } - private void resolutionMethodPco(int filter, List validIpList) { + private void resolutionMethodPco(int filter, @NonNull List validIpList) { Log.d(TAG, "PCO Method"); int PCO_ID_IPV6 = @@ -895,17 +903,34 @@ public class EpdgSelector { } } - private void getInetAddressWithPcoData(byte[] pcoData, List validIpList) { - InetAddress ipAddress; - if (pcoData != null && pcoData.length > 0) { - try { - ipAddress = InetAddress.getByAddress(pcoData); - validIpList.add(ipAddress); - } catch (Exception e) { - Log.e(TAG, "Exception when querying IP address : " + e); + private void getInetAddressWithPcoData( + List pcoData, @NonNull List validIpList) { + for (byte[] data : pcoData) { + int ipAddressLen = 0; + /* + * The PCO container contents starts with the operator MCC and MNC of size 3 bytes + * combined followed by one IPv6 or IPv4 address. + * IPv6 address is encoded as a 128-bit address and + * IPv4 address is encoded as 32-bit address. + */ + if (data.length > PCO_MCC_MNC_LEN) { + ipAddressLen = data.length - PCO_MCC_MNC_LEN; + } + if ((ipAddressLen == PCO_IPV4_LEN) || (ipAddressLen == PCO_IPV6_LEN)) { + byte[] ipAddressData = Arrays.copyOfRange(data, PCO_MCC_MNC_LEN, data.length); + try { + validIpList.add(InetAddress.getByAddress(ipAddressData)); + } catch (UnknownHostException e) { + Log.e( + TAG, + "Exception when querying IP address(" + + Arrays.toString(ipAddressData) + + "): " + + e); + } + } else { + Log.e(TAG, "Invalid PCO data:" + Arrays.toString(data)); } - } else { - Log.d(TAG, "Empty PCO data"); } } diff --git a/test/com/google/android/iwlan/IwlanBroadcastReceiverTest.java b/test/com/google/android/iwlan/IwlanBroadcastReceiverTest.java index 24be2b8..42e285e 100644 --- a/test/com/google/android/iwlan/IwlanBroadcastReceiverTest.java +++ b/test/com/google/android/iwlan/IwlanBroadcastReceiverTest.java @@ -19,6 +19,8 @@ package com.google.android.iwlan; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.*; import android.content.Context; @@ -114,6 +116,14 @@ public class IwlanBroadcastReceiverTest { mStaticMockSession.finishMocking(); } + @Test + public void testOnReceiveNoPcoData() throws Exception { + onReceiveMethodWithArgs(ApnSetting.TYPE_IMS, testPcoIdIPv6, null); + + // Verify the called times of setPcoData method + verify(mMockEpdgSelector, times(0)).setPcoData(anyInt(), any(byte[].class)); + } + @Test public void testOnReceiveIPv6Pass() throws Exception { onReceiveMethodWithArgs(ApnSetting.TYPE_IMS, testPcoIdIPv6); @@ -178,4 +188,16 @@ public class IwlanBroadcastReceiverTest { // Trigger onReceive method mBroadcastReceiver.onReceive(mMockContext, mIntent); } + + private void onReceiveMethodWithArgs(int apnType, int pcoId, byte[] pcoData) { + // Create intent object + final Intent mIntent = new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE); + mIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, testSubId); + mIntent.putExtra(EXTRA_APN_TYPE_INT_KEY, apnType); + mIntent.putExtra(EXTRA_PCO_ID_KEY, pcoId); + mIntent.putExtra(EXTRA_PCO_VALUE_KEY, pcoData); + + // Trigger onReceive method + mBroadcastReceiver.onReceive(mMockContext, mIntent); + } } diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java index 3f23dab..87825e9 100644 --- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java @@ -74,6 +74,13 @@ public class EpdgSelectorTest { private EpdgSelector mEpdgSelector; public static final int DEFAULT_SLOT_INDEX = 0; + private static final byte[] TEST_PCO_NO_DATA = {0x00}; + private static final byte[] TEST_PCO_PLMN_DATA = {0x38, 0x01, 0x24, 0x00}; + private static final byte[] TEST_PCO_IPV4_DATA = {0x38, 0x01, 0x24, 0x7F, 0x00, 0x00, 0x01}; + private static final byte[] TEST_PCO_IPV6_DATA = { + 0x38, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 + }; private static final String TEST_IP_ADDRESS = "127.0.0.1"; private static final String TEST_IP_ADDRESS_1 = "127.0.0.2"; private static final String TEST_IP_ADDRESS_2 = "127.0.0.3"; @@ -615,12 +622,8 @@ public class EpdgSelectorTest { addTestPcoIdsToTestConfigBundle(); mEpdgSelector.clearPcoData(); - boolean retIPv6 = - mEpdgSelector.setPcoData( - testPcoIdIPv6, InetAddress.getByName(TEST_IPV6_ADDRESS).getAddress()); - boolean retIPv4 = - mEpdgSelector.setPcoData( - testPcoIdIPv4, InetAddress.getByName(TEST_IP_ADDRESS).getAddress()); + assertTrue(mEpdgSelector.setPcoData(testPcoIdIPv6, TEST_PCO_IPV6_DATA)); + assertTrue(mEpdgSelector.setPcoData(testPcoIdIPv4, TEST_PCO_IPV4_DATA)); ArrayList testInetAddresses = getValidatedServerListWithDefaultParams(false /* isEmergency */); @@ -630,6 +633,40 @@ public class EpdgSelectorTest { assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IPV6_ADDRESS))); } + @Test + public void testPcoResolutionMethodWithNoPcoData() throws Exception { + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PCO}); + addTestPcoIdsToTestConfigBundle(); + + mEpdgSelector.clearPcoData(); + assertTrue(mEpdgSelector.setPcoData(testPcoIdIPv6, TEST_PCO_NO_DATA)); + assertTrue(mEpdgSelector.setPcoData(testPcoIdIPv4, TEST_PCO_NO_DATA)); + + ArrayList testInetAddresses = + getValidatedServerListWithDefaultParams(false /* isEmergency */); + + assertEquals(0, testInetAddresses.size()); + } + + @Test + public void testPcoResolutionMethodWithOnlyPlmnData() throws Exception { + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PCO}); + addTestPcoIdsToTestConfigBundle(); + + mEpdgSelector.clearPcoData(); + assertTrue(mEpdgSelector.setPcoData(testPcoIdIPv6, TEST_PCO_PLMN_DATA)); + assertTrue(mEpdgSelector.setPcoData(testPcoIdIPv4, TEST_PCO_PLMN_DATA)); + + ArrayList testInetAddresses = + getValidatedServerListWithDefaultParams(false /* isEmergency */); + + assertEquals(0, testInetAddresses.size()); + } + private void addTestPcoIdsToTestConfigBundle() { mTestBundle.putInt(CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV6_INT, testPcoIdIPv6); mTestBundle.putInt(CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV4_INT, testPcoIdIPv4); -- cgit v1.2.3 From 923ca6c1a505b4764840b83137d8e388079de74d Mon Sep 17 00:00:00 2001 From: Munikrishna Date: Thu, 23 Nov 2023 15:22:40 +0000 Subject: Add multiple SA proposals support in IWLAN Bug: 287296642 Test: atest IwlanTests Change-Id: Ibe8c66d4500638d6ee083fe7156759a644e071a2 --- flags/main.aconfig | 6 + .../android/iwlan/epdg/EpdgChildSaProposal.java | 79 ++++++++ .../android/iwlan/epdg/EpdgIkeSaProposal.java | 108 ++++++++++ .../google/android/iwlan/epdg/EpdgSaProposal.java | 223 +++++++++++++++++++++ .../android/iwlan/epdg/EpdgTunnelManager.java | 167 ++++++++++++++- .../android/iwlan/epdg/EpdgTunnelManagerTest.java | 64 ++++++ 6 files changed, 641 insertions(+), 6 deletions(-) create mode 100644 src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java create mode 100644 src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java create mode 100644 src/com/google/android/iwlan/epdg/EpdgSaProposal.java diff --git a/flags/main.aconfig b/flags/main.aconfig index b7ec6fe..de6d86f 100644 --- a/flags/main.aconfig +++ b/flags/main.aconfig @@ -12,3 +12,9 @@ flag { description: "Add AES_GCM algorithm support in IWLAN." bug: "306119890" } +flag { + name: "multiple_sa_proposals" + namespace: "iwlan_telephony" + description: "Add multiple proposals of IKE SA and Child SA" + bug: "287296642" +} diff --git a/src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java new file mode 100644 index 0000000..d85e322 --- /dev/null +++ b/src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 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.google.android.iwlan.epdg; + +import android.net.ipsec.ike.ChildSaProposal; +import android.util.Pair; + +public class EpdgChildSaProposal extends EpdgSaProposal { + /** + * Builds {@link ChildSaProposal} of carrier proposed encryption algorithms (non-AEAD) cipher + * suit. + */ + public ChildSaProposal buildProposedChildSaProposal() { + return buildProposal(false, true); + } + + /** Builds {@link ChildSaProposal} of carrier proposed AEAD algorithms cipher suit. */ + public ChildSaProposal buildProposedChildSaAeadProposal() { + return buildProposal(true, true); + } + + /** + * Builds {@link ChildSaProposal} of Iwlan supported encryption algorithms (non-AEAD) cipher + * suit. + */ + public ChildSaProposal buildSupportedChildSaProposal() { + return buildProposal(false, false); + } + + /** Builds {@link ChildSaProposal} of Iwlan supported AEAD algorithms cipher suit. */ + public ChildSaProposal buildSupportedChildSaAeadProposal() { + return buildProposal(true, false); + } + + private ChildSaProposal buildProposal(boolean isAead, boolean isProposed) { + ChildSaProposal.Builder saProposalBuilder = new ChildSaProposal.Builder(); + + int[] dhGroups = getDhGroups(); + for (int dhGroup : dhGroups) { + saProposalBuilder.addDhGroup(dhGroup); + } + + Pair[] encrAlgos; + + if (isAead) { + encrAlgos = (isProposed) ? getAeadAlgos() : getSupportedAeadAlgos(); + } else { + encrAlgos = (isProposed) ? getEncryptionAlgos() : getSupportedEncryptionAlgos(); + } + + for (Pair encrAlgo : encrAlgos) { + saProposalBuilder.addEncryptionAlgorithm(encrAlgo.first, encrAlgo.second); + } + + if (!isAead) { + int[] integrityAlgos = + (isProposed) ? getIntegrityAlgos() : getSupportedIntegrityAlgos(); + for (int integrityAlgo : integrityAlgos) { + saProposalBuilder.addIntegrityAlgorithm(integrityAlgo); + } + } + + return saProposalBuilder.build(); + } +} diff --git a/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java new file mode 100644 index 0000000..defaa30 --- /dev/null +++ b/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java @@ -0,0 +1,108 @@ +/* + * Copyright 2020 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.google.android.iwlan.epdg; + +import android.net.ipsec.ike.IkeSaProposal; +import android.util.Pair; + +import java.util.LinkedHashSet; + +public class EpdgIkeSaProposal extends EpdgSaProposal { + protected final LinkedHashSet mProposedPrfAlgos = new LinkedHashSet<>(); + + /** + * Add proposed PRF algorithms by the carrier. + * + * @param prfAlgos proposed PRF algorithms + */ + public void addProposedPrfAlgorithm(int[] prfAlgos) { + for (int prfAlgo : prfAlgos) { + if (validateConfig(prfAlgo, VALID_PRF_ALGOS, CONFIG_TYPE_PRF_ALGO)) { + mProposedPrfAlgos.add(prfAlgo); + } + } + } + + private int[] getPrfAlgos() { + return mProposedPrfAlgos.stream().mapToInt(Integer::intValue).toArray(); + } + + private int[] getSupportedPrfAlgos() { + return VALID_PRF_ALGOS.stream().mapToInt(Integer::intValue).toArray(); + } + + /** + * Builds {@link IkeSaProposal} of carrier proposed encryption algorithms (non-AEAD) cipher + * suit. + */ + public IkeSaProposal buildProposedIkeSaProposal() { + return buildProposal(false, true); + } + + /** Builds {@link IkeSaProposal} of carrier proposed AEAD algorithms cipher suit. */ + public IkeSaProposal buildProposedIkeSaAeadProposal() { + return buildProposal(true, true); + } + + /** + * Builds {@link IkeSaProposal} of Iwlan supported encryption algorithms (non-AEAD) cipher suit. + */ + public IkeSaProposal buildSupportedIkeSaProposal() { + return buildProposal(false, false); + } + + /** Builds {@link IkeSaProposal} of Iwlan supported AEAD algorithms cipher suit. */ + public IkeSaProposal buildSupportedIkeSaAeadProposal() { + return buildProposal(true, false); + } + + private IkeSaProposal buildProposal(boolean isAead, boolean isProposed) { + IkeSaProposal.Builder saProposalBuilder = new IkeSaProposal.Builder(); + + int[] dhGroups = isProposed ? getDhGroups() : getSupportedDhGroups(); + for (int dhGroup : dhGroups) { + saProposalBuilder.addDhGroup(dhGroup); + } + + Pair[] encrAlgos; + + if (isAead) { + encrAlgos = (isProposed) ? getAeadAlgos() : getSupportedAeadAlgos(); + } else { + encrAlgos = (isProposed) ? getEncryptionAlgos() : getSupportedEncryptionAlgos(); + } + + for (Pair encrAlgo : encrAlgos) { + saProposalBuilder.addEncryptionAlgorithm(encrAlgo.first, encrAlgo.second); + } + + if (!isAead) { + int[] integrityAlgos = + (isProposed) ? getIntegrityAlgos() : getSupportedIntegrityAlgos(); + for (int integrityAlgo : integrityAlgos) { + saProposalBuilder.addIntegrityAlgorithm(integrityAlgo); + } + } + + int[] prfAlgos = (isProposed) ? getPrfAlgos() : getSupportedPrfAlgos(); + for (int prfAlgo : prfAlgos) { + saProposalBuilder.addPseudorandomFunction(prfAlgo); + } + + return saProposalBuilder.build(); + } +} diff --git a/src/com/google/android/iwlan/epdg/EpdgSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgSaProposal.java new file mode 100644 index 0000000..1cc55ff --- /dev/null +++ b/src/com/google/android/iwlan/epdg/EpdgSaProposal.java @@ -0,0 +1,223 @@ +/* + * Copyright 2020 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.google.android.iwlan.epdg; + +import android.net.ipsec.ike.SaProposal; +import android.util.Log; +import android.util.Pair; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +abstract class EpdgSaProposal { + private static final String TAG = EpdgSaProposal.class.getSimpleName(); + private static final Set VALID_DH_GROUPS; + private static final Set VALID_KEY_LENGTHS; + protected static final Set VALID_PRF_ALGOS; + private static final Set VALID_INTEGRITY_ALGOS; + private static final Set VALID_ENCRYPTION_ALGOS; + private static final Set VALID_AEAD_ALGOS; + + private static final String CONFIG_TYPE_DH_GROUP = "dh group"; + private static final String CONFIG_TYPE_KEY_LEN = "algorithm key length"; + protected static final String CONFIG_TYPE_PRF_ALGO = "prf algorithm"; + private static final String CONFIG_TYPE_INTEGRITY_ALGO = "integrity algorithm"; + private static final String CONFIG_TYPE_ENCRYPT_ALGO = "encryption algorithm"; + private static final String CONFIG_TYPE_AEAD_ALGO = "AEAD algorithm"; + + static { + VALID_DH_GROUPS = + Collections.unmodifiableSet( + new LinkedHashSet( + List.of( + SaProposal.DH_GROUP_1024_BIT_MODP, + SaProposal.DH_GROUP_1536_BIT_MODP, + SaProposal.DH_GROUP_2048_BIT_MODP, + SaProposal.DH_GROUP_3072_BIT_MODP, + SaProposal.DH_GROUP_4096_BIT_MODP))); + + VALID_KEY_LENGTHS = + Collections.unmodifiableSet( + new LinkedHashSet( + List.of( + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256))); + + VALID_ENCRYPTION_ALGOS = + Collections.unmodifiableSet( + new LinkedHashSet( + List.of( + SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, + SaProposal.ENCRYPTION_ALGORITHM_AES_CTR))); + + VALID_INTEGRITY_ALGOS = + Collections.unmodifiableSet( + new LinkedHashSet( + List.of( + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, + SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256))); + + VALID_AEAD_ALGOS = + Collections.unmodifiableSet( + new LinkedHashSet( + List.of( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16))); + + VALID_PRF_ALGOS = + Collections.unmodifiableSet( + new LinkedHashSet( + List.of( + SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, + SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512))); + } + + protected final LinkedHashSet mProposedDhGroups = new LinkedHashSet<>(); + protected final LinkedHashSet mProposedIntegrityAlgos = new LinkedHashSet<>(); + protected final LinkedHashSet> mProposedEncryptAlgos = + new LinkedHashSet<>(); + protected final LinkedHashSet> mProposedAeadAlgos = + new LinkedHashSet<>(); + + /** + * Add proposed DH groups by the carrier. + * + * @param dhGroups proposed DH groups + */ + public void addProposedDhGroups(int[] dhGroups) { + for (int dhGroup : dhGroups) { + if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) { + mProposedDhGroups.add(dhGroup); + } + } + } + + /** + * Add proposed integrity algorithms by the carrier. + * + * @param integrityAlgos proposed integrity algorithms + */ + public void addProposedIntegrityAlgorithm(int[] integrityAlgos) { + for (int integrityAlgo : integrityAlgos) { + if (validateConfig(integrityAlgo, VALID_INTEGRITY_ALGOS, CONFIG_TYPE_INTEGRITY_ALGO)) { + mProposedIntegrityAlgos.add(integrityAlgo); + } + } + } + + /** + * Add proposed encryption algorithms and respective key lengths by the carrier. + * + * @param encryptionAlgo proposed encryption algorithm + * @param keyLens proposed key lengths for the encryption algorithm + */ + public void addProposedEncryptionAlgorithm(int encryptionAlgo, int[] keyLens) { + if (validateConfig(encryptionAlgo, VALID_ENCRYPTION_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { + for (int keyLen : keyLens) { + if (validateConfig(keyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) { + mProposedEncryptAlgos.add(new Pair(encryptionAlgo, keyLen)); + } + } + } + } + + /** + * Add proposed AEAD algorithms and respective key lengths by the carrier. + * + * @param aeadAlgo proposed AEAD algorithm + * @param keyLens proposed key lengths for the encryption algorithm + */ + public void addProposedAeadAlgorithm(int aeadAlgo, int[] keyLens) { + if (validateConfig(aeadAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_AEAD_ALGO)) { + for (int keyLen : keyLens) { + if (validateConfig(keyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) { + mProposedAeadAlgos.add(new Pair(aeadAlgo, keyLen)); + } + } + } + } + + protected int[] getDhGroups() { + return mProposedDhGroups.stream().mapToInt(Integer::intValue).toArray(); + } + + protected int[] getSupportedDhGroups() { + return VALID_DH_GROUPS.stream().mapToInt(Integer::intValue).toArray(); + } + + protected int[] getIntegrityAlgos() { + return mProposedIntegrityAlgos.stream().mapToInt(Integer::intValue).toArray(); + } + + protected int[] getSupportedIntegrityAlgos() { + return VALID_INTEGRITY_ALGOS.stream().mapToInt(Integer::intValue).toArray(); + } + + protected Pair[] getEncryptionAlgos() { + return mProposedEncryptAlgos.toArray(new Pair[mProposedEncryptAlgos.size()]); + } + + protected Pair[] getSupportedEncryptionAlgos() { + Pair[] encrAlgos = + new Pair[VALID_ENCRYPTION_ALGOS.size() * VALID_KEY_LENGTHS.size()]; + int index = 0; + for (int algo : VALID_ENCRYPTION_ALGOS) { + for (int len : VALID_KEY_LENGTHS) { + encrAlgos[index++] = new Pair(algo, len); + } + } + + return encrAlgos; + } + + protected Pair[] getAeadAlgos() { + return mProposedAeadAlgos.toArray(new Pair[mProposedAeadAlgos.size()]); + } + + protected Pair[] getSupportedAeadAlgos() { + Pair[] aeadAlgos = + new Pair[VALID_AEAD_ALGOS.size() * VALID_KEY_LENGTHS.size()]; + int index = 0; + for (int algo : VALID_AEAD_ALGOS) { + for (int len : VALID_KEY_LENGTHS) { + aeadAlgos[index++] = new Pair(algo, len); + } + } + + return aeadAlgos; + } + + protected static boolean validateConfig( + int config, Set validConfigValues, String configType) { + if (validConfigValues.contains(config)) { + return true; + } + + Log.e(TAG, "Invalid config value for " + configType + ":" + config); + return false; + } +} diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java index 48ef67c..c598470 100644 --- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java +++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java @@ -74,6 +74,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.google.android.iwlan.ErrorPolicyManager; +import com.google.android.iwlan.IwlanCarrierConfig; import com.google.android.iwlan.IwlanError; import com.google.android.iwlan.IwlanHelper; import com.google.android.iwlan.IwlanTunnelMetricsImpl; @@ -882,10 +883,30 @@ public class EpdgTunnelManager { new TunnelModeChildSessionParams.Builder() .setLifetimeSeconds(hardTimeSeconds, softTimeSeconds); - if (mFeatureFlags.aeadAlgosEnabled() && isChildSessionAeadAlgosAvailable()) { - childSessionParamsBuilder.addChildSaProposal(buildAeadChildSaProposal()); + if (mFeatureFlags.multipleSaProposals() + && IwlanCarrierConfig.getConfigBoolean( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL)) { + EpdgChildSaProposal epdgChildSaProposal = createEpdgChildSaProposal(); + + if (isChildSessionAeadAlgosAvailable()) { + childSessionParamsBuilder.addChildSaProposal( + epdgChildSaProposal.buildProposedChildSaAeadProposal()); + } + childSessionParamsBuilder.addChildSaProposal( + epdgChildSaProposal.buildProposedChildSaProposal()); + childSessionParamsBuilder.addChildSaProposal( + epdgChildSaProposal.buildSupportedChildSaAeadProposal()); + childSessionParamsBuilder.addChildSaProposal( + epdgChildSaProposal.buildSupportedChildSaProposal()); } else { - childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal()); + if (mFeatureFlags.aeadAlgosEnabled() && isChildSessionAeadAlgosAvailable()) { + childSessionParamsBuilder.addChildSaProposal(buildAeadChildSaProposal()); + } else { + childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal()); + } } boolean handoverIPv4Present = setupRequest.srcIpv4Address().isPresent(); @@ -1023,10 +1044,26 @@ public class EpdgTunnelManager { .setRetransmissionTimeoutsMillis(getRetransmissionTimeoutsFromConfig()) .setDpdDelaySeconds(getDpdDelayFromConfig()); - if (mFeatureFlags.aeadAlgosEnabled() && isIkeSessionAeadAlgosAvailable()) { - builder.addIkeSaProposal(buildIkeSaAeadProposal()); + if (mFeatureFlags.multipleSaProposals() + && IwlanCarrierConfig.getConfigBoolean( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL)) { + EpdgIkeSaProposal epdgIkeSaProposal = createEpdgIkeSaProposal(); + + if (isIkeSessionAeadAlgosAvailable()) { + builder.addIkeSaProposal(epdgIkeSaProposal.buildProposedIkeSaAeadProposal()); + } + builder.addIkeSaProposal(epdgIkeSaProposal.buildProposedIkeSaProposal()); + builder.addIkeSaProposal(epdgIkeSaProposal.buildSupportedIkeSaAeadProposal()); + builder.addIkeSaProposal(epdgIkeSaProposal.buildSupportedIkeSaProposal()); } else { - builder.addIkeSaProposal(buildIkeSaProposal()); + if (mFeatureFlags.aeadAlgosEnabled() && isIkeSessionAeadAlgosAvailable()) { + builder.addIkeSaProposal(buildIkeSaAeadProposal()); + } else { + builder.addIkeSaProposal(buildIkeSaProposal()); + } } if (numPdnTunnels() == 0) { @@ -1137,6 +1174,124 @@ public class EpdgTunnelManager { return IwlanHelper.getConfig(configKey, mContext, mSlotId); } + private void createEpdgSaProposal(EpdgSaProposal epdgSaProposal, boolean isChildProposal) { + epdgSaProposal.addProposedDhGroups( + IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan.KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY)); + + int[] encryptionAlgos = + isChildProposal + ? IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY) + : IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY); + + for (int encryptionAlgo : encryptionAlgos) { + if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CBC) { + int[] aesCbcKeyLens = + isChildProposal + ? IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY) + : IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY); + epdgSaProposal.addProposedEncryptionAlgorithm(encryptionAlgo, aesCbcKeyLens); + } + + if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CTR) { + int[] aesCtrKeyLens = + isChildProposal + ? IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY) + : IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY); + epdgSaProposal.addProposedEncryptionAlgorithm(encryptionAlgo, aesCtrKeyLens); + } + } + + if (encryptionAlgos.length > 0) { + epdgSaProposal.addProposedIntegrityAlgorithm( + IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY)); + } + + int[] aeadAlgos = + isChildProposal + ? IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY) + : IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY); + for (int aeadAlgo : aeadAlgos) { + if (!validateConfig(aeadAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { + continue; + } + if ((aeadAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8) + || (aeadAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12) + || (aeadAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16)) { + int[] aesGcmKeyLens = + isChildProposal + ? IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY) + : IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan + .KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY); + epdgSaProposal.addProposedAeadAlgorithm(aeadAlgo, aesGcmKeyLens); + } + } + } + + private EpdgChildSaProposal createEpdgChildSaProposal() { + EpdgChildSaProposal epdgChildSaProposal = new EpdgChildSaProposal(); + createEpdgSaProposal(epdgChildSaProposal, true); + return epdgChildSaProposal; + } + + private EpdgIkeSaProposal createEpdgIkeSaProposal() { + EpdgIkeSaProposal epdgIkeSaProposal = new EpdgIkeSaProposal(); + + createEpdgSaProposal(epdgIkeSaProposal, false); + + epdgIkeSaProposal.addProposedPrfAlgorithm( + IwlanCarrierConfig.getConfigIntArray( + mContext, + mSlotId, + CarrierConfigManager.Iwlan.KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY)); + return epdgIkeSaProposal; + } + private IkeSaProposal buildIkeSaProposal() { IkeSaProposal.Builder saProposalBuilder = new IkeSaProposal.Builder(); diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java index 0326c44..83ed29e 100644 --- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; @@ -598,6 +599,68 @@ public class EpdgTunnelManagerTest { (long) childEncrAlgos.size()); } + @Test + public void testMultipleSaProposals() throws Exception { + when(mFakeFeatureFlags.multipleSaProposals()).thenReturn(true); + final String apnName = "ims"; + PersistableBundle bundle = new PersistableBundle(); + + int[] aeadAlgos = { + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, + }; + int[] aeadAlgosKeyLens = { + SaProposal.KEY_LEN_AES_192, SaProposal.KEY_LEN_AES_256, + }; + + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY, + aeadAlgos); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, + aeadAlgosKeyLens); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY, + aeadAlgos); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, + aeadAlgosKeyLens); + + bundle.putBoolean( + CarrierConfigManager.Iwlan.KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, + true); + bundle.putBoolean( + CarrierConfigManager.Iwlan.KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, + true); + + setupMockForGetConfig(bundle); + + IkeSessionArgumentCaptors tunnelArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockDefaultNetwork); + + IkeSessionParams ikeTunnelParams = tunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue(); + + assertTrue(ikeTunnelParams.getIkeSaProposals().size() > 1); + + List> ikeAeadAlgos = + ikeTunnelParams.getIkeSaProposals().get(0).getEncryptionAlgorithms(); + assertEquals( + "Reorder higher AEAD in IKE SA mismatch", + (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, + (long) ikeAeadAlgos.get(0).first); + + ChildSessionParams childTunnelParams = + tunnelArgumentCaptors.mChildSessionParamsCaptor.getValue(); + + assertTrue(childTunnelParams.getChildSaProposals().size() > 1); + + List> childAeadAlgos = + childTunnelParams.getChildSaProposals().get(0).getEncryptionAlgorithms(); + assertEquals( + "Reorder higher AEAD in Child SA mismatch", + (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, + (long) childAeadAlgos.get(0).first); + } + @Test public void testCloseTunnelWithNoTunnelForApn() throws Exception { String testApnName = "www.xyz.com"; @@ -1156,6 +1219,7 @@ public class EpdgTunnelManagerTest { when(mMockTelephonyManager.createForSubscriptionId(DEFAULT_SUBID)) .thenReturn(mMockTelephonyManager); when(mMockCarrierConfigManager.getConfigForSubId(DEFAULT_SLOT_INDEX)).thenReturn(bundle); + when(mMockCarrierConfigManager.getConfigForSubId(anyInt(), anyString())).thenReturn(bundle); } private void setVariable(Object target, String variableName, Object value) throws Exception { -- cgit v1.2.3 From c2db9d0d92527535394b44c17419834aec5b9efc Mon Sep 17 00:00:00 2001 From: Munikrishna Date: Fri, 24 Nov 2023 14:32:17 +0000 Subject: Reorder IKE and Child SA security transforms with high secured prioritized Iwlan currently adds security transforms in the order they are configured.So, to increase high secured negotiation possible, Iwlan proiritizes the security transforms and adds the transforms in the priority manner. Bug: 306323917 Test: atest IwlanTests Change-Id: I45e17387af315a6a710155c6c2930ce3e416f011 --- flags/main.aconfig | 6 + .../android/iwlan/epdg/EpdgIkeSaProposal.java | 9 ++ .../google/android/iwlan/epdg/EpdgSaProposal.java | 138 ++++++++++++++++++--- .../android/iwlan/epdg/EpdgTunnelManager.java | 8 ++ .../android/iwlan/epdg/EpdgTunnelManagerTest.java | 83 +++++++++++++ 5 files changed, 228 insertions(+), 16 deletions(-) diff --git a/flags/main.aconfig b/flags/main.aconfig index de6d86f..6749d7f 100644 --- a/flags/main.aconfig +++ b/flags/main.aconfig @@ -18,3 +18,9 @@ flag { description: "Add multiple proposals of IKE SA and Child SA" bug: "287296642" } +flag { + name: "high_secure_transforms_prioritized" + namespace: "iwlan_telephony" + description: "Reorder IKE and Child SA security transforms with high secured as prioritized" + bug: "306323917" +} diff --git a/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java index defaa30..f4d6d56 100644 --- a/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java +++ b/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java @@ -38,6 +38,15 @@ public class EpdgIkeSaProposal extends EpdgSaProposal { } private int[] getPrfAlgos() { + if (isSaferProposalsPrioritized()) { + return mProposedPrfAlgos.stream() + .sorted( + (item1, item2) -> + compareTransformPriority(VALID_PRF_ALGOS, item1, item2)) + .mapToInt(Integer::intValue) + .toArray(); + } + return mProposedPrfAlgos.stream().mapToInt(Integer::intValue).toArray(); } diff --git a/src/com/google/android/iwlan/epdg/EpdgSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgSaProposal.java index 1cc55ff..871cc5c 100644 --- a/src/com/google/android/iwlan/epdg/EpdgSaProposal.java +++ b/src/com/google/android/iwlan/epdg/EpdgSaProposal.java @@ -21,6 +21,7 @@ import android.util.Log; import android.util.Pair; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -41,24 +42,36 @@ abstract class EpdgSaProposal { private static final String CONFIG_TYPE_ENCRYPT_ALGO = "encryption algorithm"; private static final String CONFIG_TYPE_AEAD_ALGO = "AEAD algorithm"; + private boolean mSaferAlgosPrioritized = false; + + /* + * Each transform below filled with high secured order and index of each value + * represents the priority. + * Safer transform has high priority and proposals will be orders based on + * the priority order. + * For example, DH Group 4096 has more priority compare to 3072, and 3072 + * has more priority than 2048. + * With reference to 3GPP TS 33.210 and RFC 8221, high secured transforms + * are prioritized in IKE and CHILD SA proposals. + */ static { VALID_DH_GROUPS = Collections.unmodifiableSet( new LinkedHashSet( List.of( - SaProposal.DH_GROUP_1024_BIT_MODP, - SaProposal.DH_GROUP_1536_BIT_MODP, - SaProposal.DH_GROUP_2048_BIT_MODP, + SaProposal.DH_GROUP_4096_BIT_MODP, SaProposal.DH_GROUP_3072_BIT_MODP, - SaProposal.DH_GROUP_4096_BIT_MODP))); + SaProposal.DH_GROUP_2048_BIT_MODP, + SaProposal.DH_GROUP_1536_BIT_MODP, + SaProposal.DH_GROUP_1024_BIT_MODP))); VALID_KEY_LENGTHS = Collections.unmodifiableSet( new LinkedHashSet( List.of( - SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_256, SaProposal.KEY_LEN_AES_192, - SaProposal.KEY_LEN_AES_256))); + SaProposal.KEY_LEN_AES_128))); VALID_ENCRYPTION_ALGOS = Collections.unmodifiableSet( @@ -71,29 +84,29 @@ abstract class EpdgSaProposal { Collections.unmodifiableSet( new LinkedHashSet( List.of( - SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, - SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96, - SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, - SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256))); + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, + SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96))); VALID_AEAD_ALGOS = Collections.unmodifiableSet( new LinkedHashSet( List.of( - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, - SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16))); + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8))); VALID_PRF_ALGOS = Collections.unmodifiableSet( new LinkedHashSet( List.of( - SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, - SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC, - SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512, SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384, - SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512))); + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256, + SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, + SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC))); } protected final LinkedHashSet mProposedDhGroups = new LinkedHashSet<>(); @@ -161,7 +174,70 @@ abstract class EpdgSaProposal { } } + /** Enable to reorder proposals with safer ciphers prioritized. */ + public void enableReorderingSaferProposals() { + mSaferAlgosPrioritized = true; + } + + /** + * Disable to reorder proposals with safer ciphers prioritized.Follows default configured order. + */ + public void disableReorderingSaferProposals() { + mSaferAlgosPrioritized = false; + } + + protected boolean isSaferProposalsPrioritized() { + return mSaferAlgosPrioritized; + } + + protected int getIndexOf(Set set, int value) { + Iterator itr = set.iterator(); + int index = 0; + + while (itr.hasNext()) { + if (itr.next().equals(value)) { + return index; + } + index++; + } + + return -1; + } + + /** + * Compares the priority of the transforms. + */ + protected int compareTransformPriority(Set transformGroup, int item1, int item2) { + return getIndexOf(transformGroup, item1) - getIndexOf(transformGroup, item2); + } + + /** + * Compares the priority of the encryption/AEAD transforms. + * First value in pair is encryption/AEAD algorithm and + * second value in pair is key length of that algorithm. + * If Algorithms are same then compare the priotity of the key lengths else compare + * the priority of the algorithms. + */ + protected int compareEncryptionTransformPriority( + Set algos, + Set keyLens, + Pair item1, + Pair item2) { + return item1.first.equals(item2.first) + ? getIndexOf(keyLens, item1.second) - getIndexOf(keyLens, item2.second) + : getIndexOf(algos, item1.first) - getIndexOf(algos, item2.first); + } + protected int[] getDhGroups() { + if (isSaferProposalsPrioritized()) { + return mProposedDhGroups.stream() + .sorted( + (item1, item2) -> + compareTransformPriority(VALID_DH_GROUPS, item1, item2)) + .mapToInt(Integer::intValue) + .toArray(); + } + return mProposedDhGroups.stream().mapToInt(Integer::intValue).toArray(); } @@ -170,6 +246,15 @@ abstract class EpdgSaProposal { } protected int[] getIntegrityAlgos() { + if (isSaferProposalsPrioritized()) { + return mProposedIntegrityAlgos.stream() + .sorted( + (item1, item2) -> + compareTransformPriority(VALID_INTEGRITY_ALGOS, item1, item2)) + .mapToInt(Integer::intValue) + .toArray(); + } + return mProposedIntegrityAlgos.stream().mapToInt(Integer::intValue).toArray(); } @@ -178,6 +263,18 @@ abstract class EpdgSaProposal { } protected Pair[] getEncryptionAlgos() { + if (isSaferProposalsPrioritized()) { + return mProposedEncryptAlgos.stream() + .sorted( + (item1, item2) -> + compareEncryptionTransformPriority( + VALID_ENCRYPTION_ALGOS, + VALID_KEY_LENGTHS, + item1, + item2)) + .toArray(Pair[]::new); + } + return mProposedEncryptAlgos.toArray(new Pair[mProposedEncryptAlgos.size()]); } @@ -195,6 +292,15 @@ abstract class EpdgSaProposal { } protected Pair[] getAeadAlgos() { + if (isSaferProposalsPrioritized()) { + return mProposedAeadAlgos.stream() + .sorted( + (item1, item2) -> + compareEncryptionTransformPriority( + VALID_AEAD_ALGOS, VALID_KEY_LENGTHS, item1, item2)) + .toArray(Pair[]::new); + } + return mProposedAeadAlgos.toArray(new Pair[mProposedAeadAlgos.size()]); } diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java index c598470..aa2b0da 100644 --- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java +++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java @@ -891,6 +891,10 @@ public class EpdgTunnelManager { .KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL)) { EpdgChildSaProposal epdgChildSaProposal = createEpdgChildSaProposal(); + if (mFeatureFlags.highSecureTransformsPrioritized()) { + epdgChildSaProposal.enableReorderingSaferProposals(); + } + if (isChildSessionAeadAlgosAvailable()) { childSessionParamsBuilder.addChildSaProposal( epdgChildSaProposal.buildProposedChildSaAeadProposal()); @@ -1052,6 +1056,10 @@ public class EpdgTunnelManager { .KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL)) { EpdgIkeSaProposal epdgIkeSaProposal = createEpdgIkeSaProposal(); + if (mFeatureFlags.highSecureTransformsPrioritized()) { + epdgIkeSaProposal.enableReorderingSaferProposals(); + } + if (isIkeSessionAeadAlgosAvailable()) { builder.addIkeSaProposal(epdgIkeSaProposal.buildProposedIkeSaAeadProposal()); } diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java index 83ed29e..de53193 100644 --- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java @@ -661,6 +661,89 @@ public class EpdgTunnelManagerTest { (long) childAeadAlgos.get(0).first); } + @Test + public void testSaProposalsReorder() throws Exception { + when(mFakeFeatureFlags.aeadAlgosEnabled()).thenReturn(true); + when(mFakeFeatureFlags.multipleSaProposals()).thenReturn(true); + when(mFakeFeatureFlags.highSecureTransformsPrioritized()).thenReturn(true); + + final String apnName = "ims"; + int[] aeadAlgos = { + SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, + }; + int[] aeadAlgosKeyLens = { + SaProposal.KEY_LEN_AES_128, SaProposal.KEY_LEN_AES_192, SaProposal.KEY_LEN_AES_256, + }; + + PersistableBundle bundle = new PersistableBundle(); + + bundle.putIntArray( + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, + aeadAlgos); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, + aeadAlgosKeyLens); + bundle.putIntArray( + CarrierConfigManager.Iwlan + .KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, + aeadAlgos); + bundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, + aeadAlgosKeyLens); + + bundle.putBoolean( + CarrierConfigManager.Iwlan.KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, + true); + bundle.putBoolean( + CarrierConfigManager.Iwlan.KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, + true); + + setupMockForGetConfig(bundle); + + IkeSessionArgumentCaptors tunnelArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockDefaultNetwork); + + IkeSessionParams ikeTunnelParams = tunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue(); + + assertTrue(ikeTunnelParams.getIkeSaProposals().size() > 1); + + List> ikeEncrAlgos = + ikeTunnelParams.getIkeSaProposals().get(0).getEncryptionAlgorithms(); + + assertEquals( + "Reorder bigger key length in IKE SA mismatch", + (long) SaProposal.KEY_LEN_AES_256, + (long) ikeEncrAlgos.get(0).second); + + List> ikeAeadAlgos = + ikeTunnelParams.getIkeSaProposals().get(1).getEncryptionAlgorithms(); + assertEquals( + "Reorder higher AEAD in IKE SA mismatch", + (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, + (long) ikeAeadAlgos.get(0).first); + + ChildSessionParams childTunnelParams = + tunnelArgumentCaptors.mChildSessionParamsCaptor.getValue(); + + assertTrue(childTunnelParams.getChildSaProposals().size() > 1); + + List> childEncrAlgos = + childTunnelParams.getChildSaProposals().get(0).getEncryptionAlgorithms(); + + assertEquals( + "Reorder bigger key length in Child SA mismatch", + (long) SaProposal.KEY_LEN_AES_256, + (long) childEncrAlgos.get(0).second); + + List> childAeadAlgos = + childTunnelParams.getChildSaProposals().get(1).getEncryptionAlgorithms(); + assertEquals( + "Reorder higher AEAD in Child SA mismatch", + (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, + (long) childAeadAlgos.get(0).first); + } + @Test public void testCloseTunnelWithNoTunnelForApn() throws Exception { String testApnName = "www.xyz.com"; -- cgit v1.2.3