Support for Terms & Conditions notification

- Added API to add T&C URL in the CaptivePortalData class, and to
indicate if the source is from Passpoint.
- Added source indication for the Venue URL API.
- Allow the connectivity service to send a new T&C acceptance
notification.
- Updated the merge method to prefer the Capport data over the
network agent data, if the source is not authenticated (not from
Passpoint).
- Propagate the Venue Friendly name to the captive portal activity
to be used instead of SSID, when available.

Bug: 162785447
Test: End-to-end test
Test: atest ConnectivityServiceTest
Test: atest CtsNetTestCasesLatestSdk:CaptivePortalDataTest
Test: atest NetworkNotificationManagerTest
Change-Id: I4e77c3b6c01941b03c46ad32da70c77e0fecac64
This commit is contained in:
Hai Shalom
2021-02-04 19:34:06 -08:00
parent f3163aafec
commit 7c6ab4004a
6 changed files with 260 additions and 58 deletions

View File

@@ -16,12 +16,15 @@
package android.net;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -40,10 +43,29 @@ public final class CaptivePortalData implements Parcelable {
private final long mExpiryTimeMillis;
private final boolean mCaptive;
private final String mVenueFriendlyName;
private final int mVenueInfoUrlSource;
private final int mTermsAndConditionsSource;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"CAPTIVE_PORTAL_DATA_SOURCE_"}, value = {
CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT})
public @interface CaptivePortalDataSource {}
/**
* Source of information: Other (default)
*/
public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0;
/**
* Source of information: Wi-Fi Passpoint
*/
public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1;
private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
String venueFriendlyName) {
String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) {
mRefreshTimeMillis = refreshTimeMillis;
mUserPortalUrl = userPortalUrl;
mVenueInfoUrl = venueInfoUrl;
@@ -52,11 +74,14 @@ public final class CaptivePortalData implements Parcelable {
mExpiryTimeMillis = expiryTimeMillis;
mCaptive = captive;
mVenueFriendlyName = venueFriendlyName;
mVenueInfoUrlSource = venueInfoUrlSource;
mTermsAndConditionsSource = termsAndConditionsSource;
}
private CaptivePortalData(Parcel p) {
this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
p.readLong(), p.readLong(), p.readBoolean(), p.readString());
p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(),
p.readInt());
}
@Override
@@ -74,6 +99,8 @@ public final class CaptivePortalData implements Parcelable {
dest.writeLong(mExpiryTimeMillis);
dest.writeBoolean(mCaptive);
dest.writeString(mVenueFriendlyName);
dest.writeInt(mVenueInfoUrlSource);
dest.writeInt(mTermsAndConditionsSource);
}
/**
@@ -88,6 +115,9 @@ public final class CaptivePortalData implements Parcelable {
private long mExpiryTime = -1;
private boolean mCaptive;
private String mVenueFriendlyName;
private @CaptivePortalDataSource int mVenueInfoUrlSource = CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
private @CaptivePortalDataSource int mUserPortalUrlSource =
CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
/**
* Create an empty builder.
@@ -100,8 +130,8 @@ public final class CaptivePortalData implements Parcelable {
public Builder(@Nullable CaptivePortalData data) {
if (data == null) return;
setRefreshTime(data.mRefreshTimeMillis)
.setUserPortalUrl(data.mUserPortalUrl)
.setVenueInfoUrl(data.mVenueInfoUrl)
.setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource)
.setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource)
.setSessionExtendable(data.mIsSessionExtendable)
.setBytesRemaining(data.mByteLimit)
.setExpiryTime(data.mExpiryTimeMillis)
@@ -123,7 +153,18 @@ public final class CaptivePortalData implements Parcelable {
*/
@NonNull
public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) {
return setUserPortalUrl(userPortalUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER);
}
/**
* Set the URL to be used for users to login to the portal, if captive, and the source of
* the data, see {@link CaptivePortalDataSource}
*/
@NonNull
public Builder setUserPortalUrl(@Nullable Uri userPortalUrl,
@CaptivePortalDataSource int source) {
mUserPortalUrl = userPortalUrl;
mUserPortalUrlSource = source;
return this;
}
@@ -132,7 +173,18 @@ public final class CaptivePortalData implements Parcelable {
*/
@NonNull
public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) {
return setVenueInfoUrl(venueInfoUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER);
}
/**
* Set the URL that can be used by users to view information about the network venue, and
* the source of the data, see {@link CaptivePortalDataSource}
*/
@NonNull
public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl,
@CaptivePortalDataSource int source) {
mVenueInfoUrl = venueInfoUrl;
mVenueInfoUrlSource = source;
return this;
}
@@ -188,7 +240,8 @@ public final class CaptivePortalData implements Parcelable {
public CaptivePortalData build() {
return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
mVenueFriendlyName);
mVenueFriendlyName, mVenueInfoUrlSource,
mUserPortalUrlSource);
}
}
@@ -248,6 +301,22 @@ public final class CaptivePortalData implements Parcelable {
return mCaptive;
}
/**
* Get the information source of the Venue URL
* @return The source that the Venue URL was obtained from
*/
public @CaptivePortalDataSource int getVenueInfoUrlSource() {
return mVenueInfoUrlSource;
}
/**
* Get the information source of the user portal URL
* @return The source that the user portal URL was obtained from
*/
public @CaptivePortalDataSource int getUserPortalUrlSource() {
return mTermsAndConditionsSource;
}
/**
* Get the venue friendly name
*/
@@ -272,11 +341,12 @@ public final class CaptivePortalData implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName);
mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName,
mVenueInfoUrlSource, mTermsAndConditionsSource);
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof CaptivePortalData)) return false;
final CaptivePortalData other = (CaptivePortalData) obj;
return mRefreshTimeMillis == other.mRefreshTimeMillis
@@ -286,7 +356,9 @@ public final class CaptivePortalData implements Parcelable {
&& mByteLimit == other.mByteLimit
&& mExpiryTimeMillis == other.mExpiryTimeMillis
&& mCaptive == other.mCaptive
&& Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
&& Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName)
&& mVenueInfoUrlSource == other.mVenueInfoUrlSource
&& mTermsAndConditionsSource == other.mTermsAndConditionsSource;
}
@Override
@@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable {
+ ", expiryTime: " + mExpiryTimeMillis
+ ", captive: " + mCaptive
+ ", venueFriendlyName: " + mVenueFriendlyName
+ ", venueInfoUrlSource: " + mVenueInfoUrlSource
+ ", termsAndConditionsSource: " + mTermsAndConditionsSource
+ "}";
}
}

View File

@@ -6298,20 +6298,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis()));
}
// Prioritize the user portal URL from the network agent.
if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null
|| TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) {
captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl());
// Prioritize the user portal URL from the network agent if the source is authenticated.
if (apiData.getUserPortalUrl() != null && naData.getUserPortalUrlSource()
!= CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) {
captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl(),
apiData.getUserPortalUrlSource());
}
// Prioritize the venue information URL from the network agent.
if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null
|| TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) {
captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl());
// Note that venue friendly name can only come from the network agent because it is not
// in use in RFC8908. However, if using the Capport venue URL, make sure that the
// friendly name is not set from the network agent.
captivePortalBuilder.setVenueFriendlyName(null);
// Prioritize the venue information URL from the network agent if the source is
// authenticated.
if (apiData.getVenueInfoUrl() != null && naData.getVenueInfoUrlSource()
!= CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) {
captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl(),
apiData.getVenueInfoUrlSource());
}
return captivePortalBuilder.build();
}

View File

@@ -161,13 +161,20 @@ public class NetworkNotificationManager {
if (nai != null) {
transportType = approximateTransportType(nai);
final String extraInfo = nai.networkInfo.getExtraInfo();
name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo;
if (nai.linkProperties != null && nai.linkProperties.getCaptivePortalData() != null
&& !TextUtils.isEmpty(nai.linkProperties.getCaptivePortalData()
.getVenueFriendlyName())) {
name = nai.linkProperties.getCaptivePortalData().getVenueFriendlyName();
} else {
name = TextUtils.isEmpty(extraInfo)
? WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()) : extraInfo;
}
// Only notify for Internet-capable networks.
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
} else {
// Legacy notifications.
transportType = TRANSPORT_CELLULAR;
name = null;
name = "";
}
// Clear any previous notification with lower priority, otherwise return. http://b/63676954.
@@ -193,35 +200,30 @@ public class NetworkNotificationManager {
final CharSequence details;
int icon = getIcon(transportType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
if (transportType == TRANSPORT_CELLULAR) {
title = r.getString(R.string.mobile_no_internet);
} else if (transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
title = r.getString(R.string.wifi_no_internet, name);
} else {
title = r.getString(R.string.other_networks_no_internet);
}
details = r.getString(R.string.private_dns_broken_detailed);
} else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.network_partial_connectivity,
WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
title = r.getString(R.string.network_partial_connectivity, name);
details = r.getString(R.string.network_partial_connectivity_detailed);
} else if (notifyType == NotificationType.LOST_INTERNET &&
transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.SIGN_IN) {
switch (transportType) {
case TRANSPORT_WIFI:
title = r.getString(R.string.wifi_available_sign_in, 0);
details = r.getString(R.string.network_available_sign_in_detailed,
WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
details = r.getString(R.string.network_available_sign_in_detailed, name);
break;
case TRANSPORT_CELLULAR:
title = r.getString(R.string.network_available_sign_in, 0);

View File

@@ -54,12 +54,26 @@ class CaptivePortalDataTest {
}
.build()
private val dataFromPasspoint = CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setCaptive(true)
.apply {
if (SdkLevel.isAtLeastS()) {
setVenueFriendlyName("venue friendly name")
}
}
.build()
private fun makeBuilder() = CaptivePortalData.Builder(data)
@Test
fun testParcelUnparcel() {
val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7
val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7
assertParcelSane(data, fieldCount)
assertParcelSane(dataFromPasspoint, fieldCount)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -83,6 +97,27 @@ class CaptivePortalDataTest {
assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
}
assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build())
assertNotEqualsAfterChange { it.setUserPortalUrl(
Uri.parse("https://tc.example.com/passpoint")) }
assertNotEqualsAfterChange { it.setUserPortalUrl(
Uri.parse("https://tc.example.com/passpoint"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
assertNotEqualsAfterChange { it.setUserPortalUrl(
Uri.parse("https://tc.example.com/other"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
assertNotEqualsAfterChange { it.setUserPortalUrl(
Uri.parse("https://tc.example.com/passpoint"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
assertNotEqualsAfterChange { it.setVenueInfoUrl(
Uri.parse("https://venue.example.com/passpoint")) }
assertNotEqualsAfterChange { it.setVenueInfoUrl(
Uri.parse("https://venue.example.com/other"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
assertNotEqualsAfterChange { it.setVenueInfoUrl(
Uri.parse("https://venue.example.com/passpoint"),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
}
@Test
@@ -130,6 +165,22 @@ class CaptivePortalDataTest {
assertEquals("venue friendly name", data.venueFriendlyName)
}
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
fun testGetVenueInfoUrlSource() {
assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
data.venueInfoUrlSource)
assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT,
dataFromPasspoint.venueInfoUrlSource)
}
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
fun testGetUserPortalUrlSource() {
assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
data.userPortalUrlSource)
assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT,
dataFromPasspoint.userPortalUrlSource)
}
private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
CaptivePortalData.Builder(this).apply { mutator(this) }.build()

View File

@@ -358,8 +358,15 @@ public class ConnectivityServiceTest {
private static final String INTERFACE_NAME = "interface";
private static final String TEST_VENUE_URL_NA = "https://android.com/";
private static final String TEST_VENUE_URL_NA_PASSPOINT = "https://android.com/";
private static final String TEST_VENUE_URL_NA_OTHER = "https://example.com/";
private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT =
"https://android.com/terms/";
private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER =
"https://example.com/terms/";
private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/";
private static final String TEST_USER_PORTAL_API_URL_CAPPORT =
"https://android.com/user/api/capport/";
private static final String TEST_FRIENDLY_NAME = "Network friendly name";
private static final String TEST_REDIRECT_URL = "http://example.com/firstPath";
@@ -3216,39 +3223,68 @@ public class ConnectivityServiceTest {
}
private class CaptivePortalTestData {
CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData,
CaptivePortalData expectedMergedData) {
mNaData = naData;
CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData,
CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData,
CaptivePortalData expectedMergedOtherData) {
mNaPasspointData = naPasspointData;
mCapportData = capportData;
mExpectedMergedData = expectedMergedData;
mNaOtherData = naOtherData;
mExpectedMergedPasspointData = expectedMergedPasspointData;
mExpectedMergedOtherData = expectedMergedOtherData;
}
public final CaptivePortalData mNaData;
public final CaptivePortalData mNaPasspointData;
public final CaptivePortalData mCapportData;
public final CaptivePortalData mExpectedMergedData;
public final CaptivePortalData mNaOtherData;
public final CaptivePortalData mExpectedMergedPasspointData;
public final CaptivePortalData mExpectedMergedOtherData;
}
private CaptivePortalTestData setupCaptivePortalData() {
final CaptivePortalData capportData = new CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
.setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT))
.setExpiryTime(1000000L)
.setBytesRemaining(12345L)
.build();
final CaptivePortalData naData = new CaptivePortalData.Builder()
final CaptivePortalData naPasspointData = new CaptivePortalData.Builder()
.setBytesRemaining(80802L)
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder()
final CaptivePortalData naOtherData = new CaptivePortalData.Builder()
.setBytesRemaining(80802L)
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_OTHER),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER)
.setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER)
.setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
final CaptivePortalData expectedMergedPasspointData = new CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setBytesRemaining(12345L)
.setExpiryTime(1000000L)
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT),
CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
return new CaptivePortalTestData(naData, capportData, expectedMergedData);
final CaptivePortalData expectedMergedOtherData = new CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setBytesRemaining(12345L)
.setExpiryTime(1000000L)
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
.setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT))
.setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
return new CaptivePortalTestData(naPasspointData, capportData, naOtherData,
expectedMergedPasspointData, expectedMergedOtherData);
}
@Test
@@ -3262,15 +3298,26 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
// Venue URL and friendly name from Network agent, confirm that API data gets precedence
// on the bytes remaining.
// Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm
// that API data gets precedence on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties();
linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the capport data is merged
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
lp -> captivePortalTestData.mExpectedMergedPasspointData
.equals(lp.getCaptivePortalData()));
// Now send this information from non-Passpoint source, confirm that Capport data takes
// precedence
linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the capport data is merged
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mExpectedMergedOtherData
.equals(lp.getCaptivePortalData()));
// Create a new LP with no Network agent capport data
final LinkProperties newLps = new LinkProperties();
@@ -3287,12 +3334,12 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> lp.getCaptivePortalData() == null);
newLps.setCaptivePortalData(captivePortalTestData.mNaData);
newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(newLps);
// Make sure that only the network agent capport data is available
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData()));
}
@Test
@@ -3303,12 +3350,12 @@ public class ConnectivityServiceTest {
// Venue URL and friendly name from Network agent, confirm that API data gets precedence
// on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties();
linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the data is saved correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData()));
// Expected merged data: Network agent data is preferred, and values that are not used by
// it are merged from capport data
@@ -3316,7 +3363,8 @@ public class ConnectivityServiceTest {
// Make sure that the Capport data is merged correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
lp -> captivePortalTestData.mExpectedMergedPasspointData.equals(
lp.getCaptivePortalData()));
// Now set the naData to null
linkProperties.setCaptivePortalData(null);
@@ -3327,6 +3375,32 @@ public class ConnectivityServiceTest {
lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
}
@Test
public void testMergeCaptivePortalDataFromNetworkAgentOtherSourceFirstThenCapport()
throws Exception {
final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi();
final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData();
// Venue URL and friendly name from Network agent, confirm that API data gets precedence
// on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties();
linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the data is saved correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mNaOtherData.equals(lp.getCaptivePortalData()));
// Expected merged data: Network agent data is preferred, and values that are not used by
// it are merged from capport data
mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData);
// Make sure that the Capport data is merged correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mExpectedMergedOtherData.equals(
lp.getCaptivePortalData()));
}
private NetworkRequest.Builder newWifiRequestBuilder() {
return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
}

View File

@@ -63,6 +63,8 @@ import java.util.List;
@SmallTest
public class NetworkNotificationManagerTest {
private static final String TEST_SSID = "Test SSID";
private static final String TEST_EXTRA_INFO = "extra";
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -72,6 +74,7 @@ public class NetworkNotificationManagerTest {
WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
WIFI_CAPABILITIES.setSSID(TEST_SSID);
// Set the underyling network to wifi.
VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
@@ -112,7 +115,7 @@ public class NetworkNotificationManagerTest {
when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
.thenReturn(mNotificationManager);
when(mNetworkInfo.getExtraInfo()).thenReturn("extra");
when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO);
when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);
mManager = new NetworkNotificationManager(mCtx, mTelephonyManager);
@@ -125,11 +128,11 @@ public class NetworkNotificationManagerTest {
.notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any());
final int transportType = NetworkNotificationManager.approximateTransportType(nai);
if (transportType == NetworkCapabilities.TRANSPORT_WIFI) {
verify(mResources, times(1)).getString(title, eq(any()));
verify(mResources, times(1)).getString(eq(title), eq(TEST_EXTRA_INFO));
} else {
verify(mResources, times(1)).getString(title);
}
verify(mResources, times(1)).getString(R.string.private_dns_broken_detailed);
verify(mResources, times(1)).getString(eq(R.string.private_dns_broken_detailed));
}
@Test