Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef5f5b1e authored by Hai Shalom's avatar Hai Shalom
Browse files

Support for Venue URL and friendly name from Network agent

Extend CaptivePortalData with a member to hold the venue friendly
name. If CaptivePortalData is initialized by both the network
agent and Capport, merge the two objects to include the venue
friendly name and prioritize the venue URL from the network
agent.

Bug: 162783305
Test: atest ConnectivityServiceTest
Test: atest CtsNetTestCasesLatestSdk:CaptivePortalDataTest
Test: End-to-end test
Change-Id: I4fdf356be42237c5b6c0ae5bacfd3cec4726861b
parent 469e5b33
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -5955,6 +5955,7 @@ package android.net {
    method public long getExpiryTimeMillis();
    method public long getExpiryTimeMillis();
    method public long getRefreshTimeMillis();
    method public long getRefreshTimeMillis();
    method @Nullable public android.net.Uri getUserPortalUrl();
    method @Nullable public android.net.Uri getUserPortalUrl();
    method @Nullable public String getVenueFriendlyName();
    method @Nullable public android.net.Uri getVenueInfoUrl();
    method @Nullable public android.net.Uri getVenueInfoUrl();
    method public boolean isCaptive();
    method public boolean isCaptive();
    method public boolean isSessionExtendable();
    method public boolean isSessionExtendable();
@@ -5972,6 +5973,7 @@ package android.net {
    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
    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 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);
    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);
  }
  }
+32 −6
Original line number Original line Diff line number Diff line
@@ -39,9 +39,11 @@ public final class CaptivePortalData implements Parcelable {
    private final long mByteLimit;
    private final long mByteLimit;
    private final long mExpiryTimeMillis;
    private final long mExpiryTimeMillis;
    private final boolean mCaptive;
    private final boolean mCaptive;
    private final String mVenueFriendlyName;


    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) {
        mRefreshTimeMillis = refreshTimeMillis;
        mRefreshTimeMillis = refreshTimeMillis;
        mUserPortalUrl = userPortalUrl;
        mUserPortalUrl = userPortalUrl;
        mVenueInfoUrl = venueInfoUrl;
        mVenueInfoUrl = venueInfoUrl;
@@ -49,11 +51,12 @@ public final class CaptivePortalData implements Parcelable {
        mByteLimit = byteLimit;
        mByteLimit = byteLimit;
        mExpiryTimeMillis = expiryTimeMillis;
        mExpiryTimeMillis = expiryTimeMillis;
        mCaptive = captive;
        mCaptive = captive;
        mVenueFriendlyName = venueFriendlyName;
    }
    }


    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.readLong(), p.readLong(), p.readBoolean(), p.readString());
    }
    }


    @Override
    @Override
@@ -70,6 +73,7 @@ public final class CaptivePortalData implements Parcelable {
        dest.writeLong(mByteLimit);
        dest.writeLong(mByteLimit);
        dest.writeLong(mExpiryTimeMillis);
        dest.writeLong(mExpiryTimeMillis);
        dest.writeBoolean(mCaptive);
        dest.writeBoolean(mCaptive);
        dest.writeString(mVenueFriendlyName);
    }
    }


    /**
    /**
@@ -83,6 +87,7 @@ public final class CaptivePortalData implements Parcelable {
        private long mBytesRemaining = -1;
        private long mBytesRemaining = -1;
        private long mExpiryTime = -1;
        private long mExpiryTime = -1;
        private boolean mCaptive;
        private boolean mCaptive;
        private String mVenueFriendlyName;


        /**
        /**
         * Create an empty builder.
         * Create an empty builder.
@@ -100,7 +105,8 @@ public final class CaptivePortalData implements Parcelable {
                    .setSessionExtendable(data.mIsSessionExtendable)
                    .setSessionExtendable(data.mIsSessionExtendable)
                    .setBytesRemaining(data.mByteLimit)
                    .setBytesRemaining(data.mByteLimit)
                    .setExpiryTime(data.mExpiryTimeMillis)
                    .setExpiryTime(data.mExpiryTimeMillis)
                    .setCaptive(data.mCaptive);
                    .setCaptive(data.mCaptive)
                    .setVenueFriendlyName(data.mVenueFriendlyName);
        }
        }


        /**
        /**
@@ -166,13 +172,23 @@ public final class CaptivePortalData implements Parcelable {
            return this;
            return this;
        }
        }


        /**
         * Set the venue friendly name.
         */
        @NonNull
        public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) {
            mVenueFriendlyName = venueFriendlyName;
            return this;
        }

        /**
        /**
         * Create a new {@link CaptivePortalData}.
         * Create a new {@link CaptivePortalData}.
         */
         */
        @NonNull
        @NonNull
        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);
        }
        }
    }
    }


@@ -232,6 +248,14 @@ public final class CaptivePortalData implements Parcelable {
        return mCaptive;
        return mCaptive;
    }
    }


    /**
     * Get the venue friendly name
     */
    @Nullable
    public String getVenueFriendlyName() {
        return mVenueFriendlyName;
    }

    @NonNull
    @NonNull
    public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
    public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
        @Override
        @Override
@@ -248,7 +272,7 @@ 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);
                mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName);
    }
    }


    @Override
    @Override
@@ -261,7 +285,8 @@ public final class CaptivePortalData implements Parcelable {
                && mIsSessionExtendable == other.mIsSessionExtendable
                && mIsSessionExtendable == other.mIsSessionExtendable
                && mByteLimit == other.mByteLimit
                && mByteLimit == other.mByteLimit
                && mExpiryTimeMillis == other.mExpiryTimeMillis
                && mExpiryTimeMillis == other.mExpiryTimeMillis
                && mCaptive == other.mCaptive;
                && mCaptive == other.mCaptive
                && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
    }
    }


    @Override
    @Override
@@ -274,6 +299,7 @@ public final class CaptivePortalData implements Parcelable {
                + ", byteLimit: " + mByteLimit
                + ", byteLimit: " + mByteLimit
                + ", expiryTime: " + mExpiryTimeMillis
                + ", expiryTime: " + mExpiryTimeMillis
                + ", captive: " + mCaptive
                + ", captive: " + mCaptive
                + ", venueFriendlyName: " + mVenueFriendlyName
                + "}";
                + "}";
    }
    }
}
}
+60 −6
Original line number Original line Diff line number Diff line
@@ -2969,7 +2969,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                case EVENT_CAPPORT_DATA_CHANGED: {
                case EVENT_CAPPORT_DATA_CHANGED: {
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                    if (nai == null) break;
                    if (nai == null) break;
                    handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj);
                    handleCapportApiDataUpdate(nai, (CaptivePortalData) msg.obj);
                    break;
                    break;
                }
                }
            }
            }
@@ -3304,9 +3304,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
    }
    }


    private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai,
    private void handleCapportApiDataUpdate(@NonNull final NetworkAgentInfo nai,
            @Nullable final CaptivePortalData data) {
            @Nullable final CaptivePortalData data) {
        nai.captivePortalData = data;
        nai.capportApiData = data;
        // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
        // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
    }
    }
@@ -6149,6 +6149,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
    private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
    private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
        lp.ensureDirectlyConnectedRoutes();
        lp.ensureDirectlyConnectedRoutes();
        nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
        nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
        nai.networkAgentPortalData = lp.getCaptivePortalData();
    }
    }


    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
@@ -6192,9 +6193,11 @@ public class ConnectivityService extends IConnectivityManager.Stub


        updateWakeOnLan(newLp);
        updateWakeOnLan(newLp);


        // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo,
        // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo.
        // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here.
        // It is not always contained in the LinkProperties sent from NetworkAgents, and if it
        newLp.setCaptivePortalData(networkAgent.captivePortalData);
        // does, it needs to be merged here.
        newLp.setCaptivePortalData(mergeCaptivePortalData(networkAgent.networkAgentPortalData,
                networkAgent.capportApiData));


        // TODO - move this check to cover the whole function
        // TODO - move this check to cover the whole function
        if (!Objects.equals(newLp, oldLp)) {
        if (!Objects.equals(newLp, oldLp)) {
@@ -6214,6 +6217,57 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
        mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
    }
    }


    /**
     * @param naData captive portal data from NetworkAgent
     * @param apiData captive portal data from capport API
     */
    @Nullable
    private CaptivePortalData mergeCaptivePortalData(CaptivePortalData naData,
            CaptivePortalData apiData) {
        if (naData == null || apiData == null) {
            return naData == null ? apiData : naData;
        }
        final CaptivePortalData.Builder captivePortalBuilder =
                new CaptivePortalData.Builder(naData);

        if (apiData.isCaptive()) {
            captivePortalBuilder.setCaptive(true);
        }
        if (apiData.isSessionExtendable()) {
            captivePortalBuilder.setSessionExtendable(true);
        }
        if (apiData.getExpiryTimeMillis() >= 0 || apiData.getByteLimit() >= 0) {
            // Expiry time, bytes remaining, refresh time all need to come from the same source,
            // otherwise data would be inconsistent. Prefer the capport API info if present,
            // as it can generally be refreshed more often.
            captivePortalBuilder.setExpiryTime(apiData.getExpiryTimeMillis());
            captivePortalBuilder.setBytesRemaining(apiData.getByteLimit());
            captivePortalBuilder.setRefreshTime(apiData.getRefreshTimeMillis());
        } else if (naData.getExpiryTimeMillis() < 0 && naData.getByteLimit() < 0) {
            // No source has time / bytes remaining information: surface the newest refresh time
            // for other fields
            captivePortalBuilder.setRefreshTime(
                    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);
        }
        return captivePortalBuilder.build();
    }

    private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
    private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
        // Marks are only available on WiFi interfaces. Checking for
        // Marks are only available on WiFi interfaces. Checking for
        // marks on unsupported interfaces is harmless.
        // marks on unsupported interfaces is harmless.
+7 −2
Original line number Original line Diff line number Diff line
@@ -175,13 +175,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
    // Set to true when partial connectivity was detected.
    // Set to true when partial connectivity was detected.
    public boolean partialConnectivity;
    public boolean partialConnectivity;


    // Captive portal info of the network, if any.
    // Captive portal info of the network from RFC8908, if any.
    // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
    // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
    public CaptivePortalData captivePortalData;
    public CaptivePortalData capportApiData;


    // The UID of the remote entity that created this Network.
    // The UID of the remote entity that created this Network.
    public final int creatorUid;
    public final int creatorUid;


    // Network agent portal info of the network, if any. This information is provided from
    // non-RFC8908 sources, such as Wi-Fi Passpoint, which can provide information such as Venue
    // URL, Terms & Conditions URL, and network friendly name.
    public CaptivePortalData networkAgentPortalData;

    // Networks are lingered when they become unneeded as a result of their NetworkRequests being
    // Networks are lingered when they become unneeded as a result of their NetworkRequests being
    // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
    // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
    // network is taken down.  This usually only happens to the default network. Lingering ends with
    // network is taken down.  This usually only happens to the default network. Lingering ends with
+9 −1
Original line number Original line Diff line number Diff line
@@ -41,13 +41,14 @@ class CaptivePortalDataTest {
            .setBytesRemaining(456L)
            .setBytesRemaining(456L)
            .setExpiryTime(789L)
            .setExpiryTime(789L)
            .setCaptive(true)
            .setCaptive(true)
            .setVenueFriendlyName("venue friendly name")
            .build()
            .build()


    private fun makeBuilder() = CaptivePortalData.Builder(data)
    private fun makeBuilder() = CaptivePortalData.Builder(data)


    @Test
    @Test
    fun testParcelUnparcel() {
    fun testParcelUnparcel() {
        assertParcelSane(data, fieldCount = 7)
        assertParcelSane(data, fieldCount = 8)


        assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
        assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
        assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
        assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -66,6 +67,8 @@ class CaptivePortalDataTest {
        assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
        assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
        assertNotEqualsAfterChange { it.setExpiryTime(12L) }
        assertNotEqualsAfterChange { it.setExpiryTime(12L) }
        assertNotEqualsAfterChange { it.setCaptive(false) }
        assertNotEqualsAfterChange { it.setCaptive(false) }
        assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
        assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
    }
    }


    @Test
    @Test
@@ -108,6 +111,11 @@ class CaptivePortalDataTest {
        assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
        assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
    }
    }


    @Test
    fun testVenueFriendlyName() {
        assertEquals("venue friendly name", data.venueFriendlyName)
    }

    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()


Loading