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:
@@ -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
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user