Loading core/api/system-current.txt +6 −0 Original line number Diff line number Diff line Loading @@ -6006,11 +6006,15 @@ package android.net { method public long getExpiryTimeMillis(); method public long getRefreshTimeMillis(); method @Nullable public android.net.Uri getUserPortalUrl(); method public int getUserPortalUrlSource(); method @Nullable public String getVenueFriendlyName(); method @Nullable public android.net.Uri getVenueInfoUrl(); method public int getVenueInfoUrlSource(); method public boolean isCaptive(); method public boolean isSessionExtendable(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; } Loading @@ -6024,8 +6028,10 @@ package android.net { method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); } public class ConnectivityManager { Loading packages/Connectivity/framework/src/android/net/CaptivePortalData.java +81 −7 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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; Loading @@ -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 Loading @@ -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); } /** Loading @@ -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. Loading @@ -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) Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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); } } Loading Loading @@ -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 */ Loading @@ -272,7 +341,8 @@ 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 Loading @@ -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 Loading @@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable { + ", expiryTime: " + mExpiryTimeMillis + ", captive: " + mCaptive + ", venueFriendlyName: " + mVenueFriendlyName + ", venueInfoUrlSource: " + mVenueInfoUrlSource + ", termsAndConditionsSource: " + mTermsAndConditionsSource + "}"; } } services/core/java/com/android/server/ConnectivityService.java +12 −14 Original line number Diff line number Diff line Loading @@ -6407,20 +6407,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 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 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 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(); } Loading services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +14 −12 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); Loading tests/net/common/java/android/net/CaptivePortalDataTest.kt +52 −1 Original line number Diff line number Diff line Loading @@ -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()) Loading @@ -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 Loading Loading @@ -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() Loading Loading
core/api/system-current.txt +6 −0 Original line number Diff line number Diff line Loading @@ -6006,11 +6006,15 @@ package android.net { method public long getExpiryTimeMillis(); method public long getRefreshTimeMillis(); method @Nullable public android.net.Uri getUserPortalUrl(); method public int getUserPortalUrlSource(); method @Nullable public String getVenueFriendlyName(); method @Nullable public android.net.Uri getVenueInfoUrl(); method public int getVenueInfoUrlSource(); method public boolean isCaptive(); method public boolean isSessionExtendable(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; } Loading @@ -6024,8 +6028,10 @@ package android.net { method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); } public class ConnectivityManager { Loading
packages/Connectivity/framework/src/android/net/CaptivePortalData.java +81 −7 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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; Loading @@ -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 Loading @@ -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); } /** Loading @@ -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. Loading @@ -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) Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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); } } Loading Loading @@ -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 */ Loading @@ -272,7 +341,8 @@ 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 Loading @@ -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 Loading @@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable { + ", expiryTime: " + mExpiryTimeMillis + ", captive: " + mCaptive + ", venueFriendlyName: " + mVenueFriendlyName + ", venueInfoUrlSource: " + mVenueInfoUrlSource + ", termsAndConditionsSource: " + mTermsAndConditionsSource + "}"; } }
services/core/java/com/android/server/ConnectivityService.java +12 −14 Original line number Diff line number Diff line Loading @@ -6407,20 +6407,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 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 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 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(); } Loading
services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +14 −12 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); Loading
tests/net/common/java/android/net/CaptivePortalDataTest.kt +52 −1 Original line number Diff line number Diff line Loading @@ -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()) Loading @@ -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 Loading Loading @@ -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() Loading