1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.Objects;
29 
30 /**
31  * Metadata sent by captive portals, see https://www.ietf.org/id/draft-ietf-capport-api-03.txt.
32  * @hide
33  */
34 @SystemApi
35 public final class CaptivePortalData implements Parcelable {
36     private final long mRefreshTimeMillis;
37     @Nullable
38     private final Uri mUserPortalUrl;
39     @Nullable
40     private final Uri mVenueInfoUrl;
41     private final boolean mIsSessionExtendable;
42     private final long mByteLimit;
43     private final long mExpiryTimeMillis;
44     private final boolean mCaptive;
45     private final String mVenueFriendlyName;
46     private final int mVenueInfoUrlSource;
47     private final int mUserPortalUrlSource;
48 
49     /** @hide */
50     @Retention(RetentionPolicy.SOURCE)
51     @IntDef(prefix = {"CAPTIVE_PORTAL_DATA_SOURCE_"}, value = {
52             CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
53             CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT})
54     public @interface CaptivePortalDataSource {}
55 
56     /**
57      * Source of information: Other (default)
58      */
59     public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0;
60 
61     /**
62      * Source of information: Wi-Fi Passpoint
63      */
64     public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1;
65 
66     private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
67             boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
68             CharSequence venueFriendlyName, int venueInfoUrlSource, int userPortalUrlSource) {
69         mRefreshTimeMillis = refreshTimeMillis;
70         mUserPortalUrl = userPortalUrl;
71         mVenueInfoUrl = venueInfoUrl;
72         mIsSessionExtendable = isSessionExtendable;
73         mByteLimit = byteLimit;
74         mExpiryTimeMillis = expiryTimeMillis;
75         mCaptive = captive;
76         mVenueFriendlyName = venueFriendlyName == null ? null : venueFriendlyName.toString();
77         mVenueInfoUrlSource = venueInfoUrlSource;
78         mUserPortalUrlSource = userPortalUrlSource;
79     }
80 
81     private CaptivePortalData(Parcel p) {
82         this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
83                 p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(),
84                 p.readInt());
85     }
86 
87     @Override
88     public int describeContents() {
89         return 0;
90     }
91 
92     @Override
93     public void writeToParcel(@NonNull Parcel dest, int flags) {
94         dest.writeLong(mRefreshTimeMillis);
95         dest.writeParcelable(mUserPortalUrl, 0);
96         dest.writeParcelable(mVenueInfoUrl, 0);
97         dest.writeBoolean(mIsSessionExtendable);
98         dest.writeLong(mByteLimit);
99         dest.writeLong(mExpiryTimeMillis);
100         dest.writeBoolean(mCaptive);
101         dest.writeString(mVenueFriendlyName);
102         dest.writeInt(mVenueInfoUrlSource);
103         dest.writeInt(mUserPortalUrlSource);
104     }
105 
106     /**
107      * A builder to create new {@link CaptivePortalData}.
108      */
109     public static class Builder {
110         private long mRefreshTime;
111         private Uri mUserPortalUrl;
112         private Uri mVenueInfoUrl;
113         private boolean mIsSessionExtendable;
114         private long mBytesRemaining = -1;
115         private long mExpiryTime = -1;
116         private boolean mCaptive;
117         private CharSequence mVenueFriendlyName;
118         private @CaptivePortalDataSource int mVenueInfoUrlSource = CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
119         private @CaptivePortalDataSource int mUserPortalUrlSource =
120                 CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
121 
122         /**
123          * Create an empty builder.
124          */
125         public Builder() {}
126 
127         /**
128          * Create a builder copying all data from existing {@link CaptivePortalData}.
129          */
130         public Builder(@Nullable CaptivePortalData data) {
131             if (data == null) return;
132             setRefreshTime(data.mRefreshTimeMillis)
133                     .setUserPortalUrl(data.mUserPortalUrl, data.mUserPortalUrlSource)
134                     .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource)
135                     .setSessionExtendable(data.mIsSessionExtendable)
136                     .setBytesRemaining(data.mByteLimit)
137                     .setExpiryTime(data.mExpiryTimeMillis)
138                     .setCaptive(data.mCaptive)
139                     .setVenueFriendlyName(data.mVenueFriendlyName);
140         }
141 
142         /**
143          * Set the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
144          */
145         @NonNull
146         public Builder setRefreshTime(long refreshTime) {
147             mRefreshTime = refreshTime;
148             return this;
149         }
150 
151         /**
152          * Set the URL to be used for users to login to the portal, if captive.
153          */
154         @NonNull
155         public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) {
156             return setUserPortalUrl(userPortalUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER);
157         }
158 
159         /**
160          * Set the URL to be used for users to login to the portal, if captive, and the source of
161          * the data, see {@link CaptivePortalDataSource}
162          */
163         @NonNull
164         public Builder setUserPortalUrl(@Nullable Uri userPortalUrl,
165                 @CaptivePortalDataSource int source) {
166             mUserPortalUrl = userPortalUrl;
167             mUserPortalUrlSource = source;
168             return this;
169         }
170 
171         /**
172          * Set the URL that can be used by users to view information about the network venue.
173          */
174         @NonNull
175         public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) {
176             return setVenueInfoUrl(venueInfoUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER);
177         }
178 
179         /**
180          * Set the URL that can be used by users to view information about the network venue, and
181          * the source of the data, see {@link CaptivePortalDataSource}
182          */
183         @NonNull
184         public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl,
185                 @CaptivePortalDataSource int source) {
186             mVenueInfoUrl = venueInfoUrl;
187             mVenueInfoUrlSource = source;
188             return this;
189         }
190 
191         /**
192          * Set whether the portal supports extending a user session on the portal URL page.
193          */
194         @NonNull
195         public Builder setSessionExtendable(boolean sessionExtendable) {
196             mIsSessionExtendable = sessionExtendable;
197             return this;
198         }
199 
200         /**
201          * Set the number of bytes remaining on the network before the portal closes.
202          */
203         @NonNull
204         public Builder setBytesRemaining(long bytesRemaining) {
205             mBytesRemaining = bytesRemaining;
206             return this;
207         }
208 
209         /**
210          * Set the time at the session will expire, as per {@link System#currentTimeMillis()}.
211          */
212         @NonNull
213         public Builder setExpiryTime(long expiryTime) {
214             mExpiryTime = expiryTime;
215             return this;
216         }
217 
218         /**
219          * Set whether the network is captive (portal closed).
220          */
221         @NonNull
222         public Builder setCaptive(boolean captive) {
223             mCaptive = captive;
224             return this;
225         }
226 
227         /**
228          * Set the venue friendly name.
229          */
230         @NonNull
231         public Builder setVenueFriendlyName(@Nullable CharSequence venueFriendlyName) {
232             mVenueFriendlyName = venueFriendlyName;
233             return this;
234         }
235 
236         /**
237          * Create a new {@link CaptivePortalData}.
238          */
239         @NonNull
240         public CaptivePortalData build() {
241             return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
242                     mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
243                     mVenueFriendlyName, mVenueInfoUrlSource,
244                     mUserPortalUrlSource);
245         }
246     }
247 
248     /**
249      * Get the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
250      */
251     public long getRefreshTimeMillis() {
252         return mRefreshTimeMillis;
253     }
254 
255     /**
256      * Get the URL to be used for users to login to the portal, or extend their session if
257      * {@link #isSessionExtendable()} is true.
258      */
259     @Nullable
260     public Uri getUserPortalUrl() {
261         return mUserPortalUrl;
262     }
263 
264     /**
265      * Get the URL that can be used by users to view information about the network venue.
266      */
267     @Nullable
268     public Uri getVenueInfoUrl() {
269         return mVenueInfoUrl;
270     }
271 
272     /**
273      * Indicates whether the user portal URL can be used to extend sessions, when the user is logged
274      * in and the session has a time or byte limit.
275      */
276     public boolean isSessionExtendable() {
277         return mIsSessionExtendable;
278     }
279 
280     /**
281      * Get the remaining bytes on the captive portal session, at the time {@link CaptivePortalData}
282      * was refreshed. This may be different from the limit currently enforced by the portal.
283      * @return The byte limit, or -1 if not set.
284      */
285     public long getByteLimit() {
286         return mByteLimit;
287     }
288 
289     /**
290      * Get the time at the session will expire, as per {@link System#currentTimeMillis()}.
291      * @return The expiry time, or -1 if unset.
292      */
293     public long getExpiryTimeMillis() {
294         return mExpiryTimeMillis;
295     }
296 
297     /**
298      * Get whether the network is captive (portal closed).
299      */
300     public boolean isCaptive() {
301         return mCaptive;
302     }
303 
304     /**
305      * Get the information source of the Venue URL
306      * @return The source that the Venue URL was obtained from
307      */
308     public @CaptivePortalDataSource int getVenueInfoUrlSource() {
309         return mVenueInfoUrlSource;
310     }
311 
312     /**
313      * Get the information source of the user portal URL
314      * @return The source that the user portal URL was obtained from
315      */
316     public @CaptivePortalDataSource int getUserPortalUrlSource() {
317         return mUserPortalUrlSource;
318     }
319 
320     /**
321      * Get the venue friendly name
322      */
323     @Nullable
324     public CharSequence getVenueFriendlyName() {
325         return mVenueFriendlyName;
326     }
327 
328     @NonNull
329     public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
330         @Override
331         public CaptivePortalData createFromParcel(Parcel source) {
332             return new CaptivePortalData(source);
333         }
334 
335         @Override
336         public CaptivePortalData[] newArray(int size) {
337             return new CaptivePortalData[size];
338         }
339     };
340 
341     @Override
342     public int hashCode() {
343         return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
344                 mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName,
345                 mVenueInfoUrlSource, mUserPortalUrlSource);
346     }
347 
348     @Override
349     public boolean equals(@Nullable Object obj) {
350         if (!(obj instanceof CaptivePortalData)) return false;
351         final CaptivePortalData other = (CaptivePortalData) obj;
352         return mRefreshTimeMillis == other.mRefreshTimeMillis
353                 && Objects.equals(mUserPortalUrl, other.mUserPortalUrl)
354                 && Objects.equals(mVenueInfoUrl, other.mVenueInfoUrl)
355                 && mIsSessionExtendable == other.mIsSessionExtendable
356                 && mByteLimit == other.mByteLimit
357                 && mExpiryTimeMillis == other.mExpiryTimeMillis
358                 && mCaptive == other.mCaptive
359                 && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName)
360                 && mVenueInfoUrlSource == other.mVenueInfoUrlSource
361                 && mUserPortalUrlSource == other.mUserPortalUrlSource;
362     }
363 
364     @Override
365     public String toString() {
366         return "CaptivePortalData {"
367                 + "refreshTime: " + mRefreshTimeMillis
368                 + ", userPortalUrl: " + mUserPortalUrl
369                 + ", venueInfoUrl: " + mVenueInfoUrl
370                 + ", isSessionExtendable: " + mIsSessionExtendable
371                 + ", byteLimit: " + mByteLimit
372                 + ", expiryTime: " + mExpiryTimeMillis
373                 + ", captive: " + mCaptive
374                 + ", venueFriendlyName: " + mVenueFriendlyName
375                 + ", venueInfoUrlSource: " + mVenueInfoUrlSource
376                 + ", userPortalUrlSource: " + mUserPortalUrlSource
377                 + "}";
378     }
379 }
380