Merge "Support for Terms & Conditions notification"

This commit is contained in:
Hai Shalom
2021-02-10 15:36:58 +00:00
committed by Gerrit Code Review
6 changed files with 259 additions and 57 deletions

View File

@@ -16,12 +16,15 @@
package android.net; package android.net;
import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -40,10 +43,29 @@ public final class CaptivePortalData implements Parcelable {
private final long mExpiryTimeMillis; private final long mExpiryTimeMillis;
private final boolean mCaptive; private final boolean mCaptive;
private final String mVenueFriendlyName; 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, private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
String venueFriendlyName) { String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) {
mRefreshTimeMillis = refreshTimeMillis; mRefreshTimeMillis = refreshTimeMillis;
mUserPortalUrl = userPortalUrl; mUserPortalUrl = userPortalUrl;
mVenueInfoUrl = venueInfoUrl; mVenueInfoUrl = venueInfoUrl;
@@ -52,11 +74,14 @@ public final class CaptivePortalData implements Parcelable {
mExpiryTimeMillis = expiryTimeMillis; mExpiryTimeMillis = expiryTimeMillis;
mCaptive = captive; mCaptive = captive;
mVenueFriendlyName = venueFriendlyName; mVenueFriendlyName = venueFriendlyName;
mVenueInfoUrlSource = venueInfoUrlSource;
mTermsAndConditionsSource = termsAndConditionsSource;
} }
private CaptivePortalData(Parcel p) { private CaptivePortalData(Parcel p) {
this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(), 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 @Override
@@ -74,6 +99,8 @@ public final class CaptivePortalData implements Parcelable {
dest.writeLong(mExpiryTimeMillis); dest.writeLong(mExpiryTimeMillis);
dest.writeBoolean(mCaptive); dest.writeBoolean(mCaptive);
dest.writeString(mVenueFriendlyName); dest.writeString(mVenueFriendlyName);
dest.writeInt(mVenueInfoUrlSource);
dest.writeInt(mTermsAndConditionsSource);
} }
/** /**
@@ -88,6 +115,9 @@ public final class CaptivePortalData implements Parcelable {
private long mExpiryTime = -1; private long mExpiryTime = -1;
private boolean mCaptive; private boolean mCaptive;
private String mVenueFriendlyName; 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. * Create an empty builder.
@@ -100,8 +130,8 @@ public final class CaptivePortalData implements Parcelable {
public Builder(@Nullable CaptivePortalData data) { public Builder(@Nullable CaptivePortalData data) {
if (data == null) return; if (data == null) return;
setRefreshTime(data.mRefreshTimeMillis) setRefreshTime(data.mRefreshTimeMillis)
.setUserPortalUrl(data.mUserPortalUrl) .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource)
.setVenueInfoUrl(data.mVenueInfoUrl) .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource)
.setSessionExtendable(data.mIsSessionExtendable) .setSessionExtendable(data.mIsSessionExtendable)
.setBytesRemaining(data.mByteLimit) .setBytesRemaining(data.mByteLimit)
.setExpiryTime(data.mExpiryTimeMillis) .setExpiryTime(data.mExpiryTimeMillis)
@@ -123,7 +153,18 @@ public final class CaptivePortalData implements Parcelable {
*/ */
@NonNull @NonNull
public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) { 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; mUserPortalUrl = userPortalUrl;
mUserPortalUrlSource = source;
return this; return this;
} }
@@ -132,7 +173,18 @@ public final class CaptivePortalData implements Parcelable {
*/ */
@NonNull @NonNull
public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) { 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; mVenueInfoUrl = venueInfoUrl;
mVenueInfoUrlSource = source;
return this; return this;
} }
@@ -188,7 +240,8 @@ public final class CaptivePortalData implements Parcelable {
public CaptivePortalData build() { public CaptivePortalData build() {
return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl, return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive, mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
mVenueFriendlyName); mVenueFriendlyName, mVenueInfoUrlSource,
mUserPortalUrlSource);
} }
} }
@@ -248,6 +301,22 @@ public final class CaptivePortalData implements Parcelable {
return mCaptive; 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 * Get the venue friendly name
*/ */
@@ -272,7 +341,8 @@ public final class CaptivePortalData implements Parcelable {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName); mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName,
mVenueInfoUrlSource, mTermsAndConditionsSource);
} }
@Override @Override
@@ -286,7 +356,9 @@ public final class CaptivePortalData implements Parcelable {
&& mByteLimit == other.mByteLimit && mByteLimit == other.mByteLimit
&& mExpiryTimeMillis == other.mExpiryTimeMillis && mExpiryTimeMillis == other.mExpiryTimeMillis
&& mCaptive == other.mCaptive && mCaptive == other.mCaptive
&& Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName); && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName)
&& mVenueInfoUrlSource == other.mVenueInfoUrlSource
&& mTermsAndConditionsSource == other.mTermsAndConditionsSource;
} }
@Override @Override
@@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable {
+ ", expiryTime: " + mExpiryTimeMillis + ", expiryTime: " + mExpiryTimeMillis
+ ", captive: " + mCaptive + ", captive: " + mCaptive
+ ", venueFriendlyName: " + mVenueFriendlyName + ", venueFriendlyName: " + mVenueFriendlyName
+ ", venueInfoUrlSource: " + mVenueInfoUrlSource
+ ", termsAndConditionsSource: " + mTermsAndConditionsSource
+ "}"; + "}";
} }
} }

View File

@@ -6407,20 +6407,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis())); Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis()));
} }
// Prioritize the user portal URL from the network agent. // Prioritize the user portal URL from the network agent if the source is authenticated.
if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null if (apiData.getUserPortalUrl() != null && naData.getUserPortalUrlSource()
|| TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) { != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) {
captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl()); captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl(),
apiData.getUserPortalUrlSource());
} }
// Prioritize the venue information URL from the network agent. // Prioritize the venue information URL from the network agent if the source is
if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null // authenticated.
|| TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) { if (apiData.getVenueInfoUrl() != null && naData.getVenueInfoUrlSource()
captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl()); != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) {
captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl(),
// Note that venue friendly name can only come from the network agent because it is not apiData.getVenueInfoUrlSource());
// 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);
} }
return captivePortalBuilder.build(); return captivePortalBuilder.build();
} }

View File

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

View File

@@ -54,12 +54,26 @@ class CaptivePortalDataTest {
} }
.build() .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) private fun makeBuilder() = CaptivePortalData.Builder(data)
@Test @Test
fun testParcelUnparcel() { fun testParcelUnparcel() {
val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7 val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7
assertParcelSane(data, fieldCount) assertParcelSane(data, fieldCount)
assertParcelSane(dataFromPasspoint, fieldCount)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -83,6 +97,27 @@ class CaptivePortalDataTest {
assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } 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 @Test
@@ -130,6 +165,22 @@ class CaptivePortalDataTest {
assertEquals("venue friendly name", data.venueFriendlyName) 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) = private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
CaptivePortalData.Builder(this).apply { mutator(this) }.build() CaptivePortalData.Builder(this).apply { mutator(this) }.build()

View File

@@ -362,8 +362,15 @@ public class ConnectivityServiceTest {
private static final String INTERFACE_NAME = "interface"; 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_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_FRIENDLY_NAME = "Network friendly name";
private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; private static final String TEST_REDIRECT_URL = "http://example.com/firstPath";
@@ -3289,39 +3296,68 @@ public class ConnectivityServiceTest {
} }
private class CaptivePortalTestData { private class CaptivePortalTestData {
CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData, CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData,
CaptivePortalData expectedMergedData) { CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData,
mNaData = naData; CaptivePortalData expectedMergedOtherData) {
mNaPasspointData = naPasspointData;
mCapportData = capportData; 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 mCapportData;
public final CaptivePortalData mExpectedMergedData; public final CaptivePortalData mNaOtherData;
public final CaptivePortalData mExpectedMergedPasspointData;
public final CaptivePortalData mExpectedMergedOtherData;
} }
private CaptivePortalTestData setupCaptivePortalData() { private CaptivePortalTestData setupCaptivePortalData() {
final CaptivePortalData capportData = new CaptivePortalData.Builder() final CaptivePortalData capportData = new CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
.setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT))
.setExpiryTime(1000000L) .setExpiryTime(1000000L)
.setBytesRemaining(12345L) .setBytesRemaining(12345L)
.build(); .build();
final CaptivePortalData naData = new CaptivePortalData.Builder() final CaptivePortalData naPasspointData = new CaptivePortalData.Builder()
.setBytesRemaining(80802L) .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(); .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)) .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setBytesRemaining(12345L) .setBytesRemaining(12345L)
.setExpiryTime(1000000L) .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(); .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 @Test
@@ -3335,15 +3371,26 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
// Venue URL and friendly name from Network agent, confirm that API data gets precedence // Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm
// on the bytes remaining. // that API data gets precedence on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties(); final LinkProperties linkProperties = new LinkProperties();
linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties); mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the capport data is merged // Make sure that the capport data is merged
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, 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 // Create a new LP with no Network agent capport data
final LinkProperties newLps = new LinkProperties(); final LinkProperties newLps = new LinkProperties();
@@ -3360,12 +3407,12 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> lp.getCaptivePortalData() == null); lp -> lp.getCaptivePortalData() == null);
newLps.setCaptivePortalData(captivePortalTestData.mNaData); newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(newLps); mWiFiNetworkAgent.sendLinkProperties(newLps);
// Make sure that only the network agent capport data is available // Make sure that only the network agent capport data is available
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData()));
} }
@Test @Test
@@ -3376,12 +3423,12 @@ public class ConnectivityServiceTest {
// Venue URL and friendly name from Network agent, confirm that API data gets precedence // Venue URL and friendly name from Network agent, confirm that API data gets precedence
// on the bytes remaining. // on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties(); final LinkProperties linkProperties = new LinkProperties();
linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties); mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the data is saved correctly // Make sure that the data is saved correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, 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 // Expected merged data: Network agent data is preferred, and values that are not used by
// it are merged from capport data // it are merged from capport data
@@ -3389,7 +3436,8 @@ public class ConnectivityServiceTest {
// Make sure that the Capport data is merged correctly // Make sure that the Capport data is merged correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); lp -> captivePortalTestData.mExpectedMergedPasspointData.equals(
lp.getCaptivePortalData()));
// Now set the naData to null // Now set the naData to null
linkProperties.setCaptivePortalData(null); linkProperties.setCaptivePortalData(null);
@@ -3400,6 +3448,32 @@ public class ConnectivityServiceTest {
lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); 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() { private NetworkRequest.Builder newWifiRequestBuilder() {
return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
} }

View File

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