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