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 com.android.networkstack.apishim.api30; 18 19 import android.net.CaptivePortalData; 20 import android.net.INetworkMonitorCallbacks; 21 import android.net.Uri; 22 import android.os.Build; 23 import android.os.RemoteException; 24 25 import androidx.annotation.ChecksSdkIntAtLeast; 26 import androidx.annotation.NonNull; 27 import androidx.annotation.RequiresApi; 28 29 import com.android.networkstack.apishim.common.CaptivePortalDataShim; 30 import com.android.networkstack.apishim.common.ShimUtils; 31 import com.android.networkstack.apishim.common.UnsupportedApiLevelException; 32 33 import org.json.JSONException; 34 import org.json.JSONObject; 35 36 /** 37 * Compatibility implementation of {@link CaptivePortalDataShim}. 38 */ 39 @RequiresApi(Build.VERSION_CODES.R) 40 public class CaptivePortalDataShimImpl 41 extends com.android.networkstack.apishim.api29.CaptivePortalDataShimImpl { 42 @NonNull 43 protected final CaptivePortalData mData; 44 CaptivePortalDataShimImpl(@onNull CaptivePortalData data)45 public CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { 46 mData = data; 47 } 48 getData()49 public CaptivePortalData getData() { 50 return mData; 51 } 52 53 /** 54 * Parse a {@link CaptivePortalDataShim} from a JSON object. 55 * @throws JSONException The JSON is not a representation of correct captive portal data. 56 * @throws UnsupportedApiLevelException CaptivePortalData is not available on this API level. 57 */ 58 @RequiresApi(Build.VERSION_CODES.Q) 59 @NonNull fromJson(JSONObject obj)60 public static CaptivePortalDataShim fromJson(JSONObject obj) throws JSONException, 61 UnsupportedApiLevelException { 62 if (!isSupported()) { 63 return com.android.networkstack.apishim.api29.CaptivePortalDataShimImpl.fromJson(obj); 64 } 65 66 final long refreshTimeMs = System.currentTimeMillis(); 67 final long secondsRemaining = getLongOrDefault(obj, "seconds-remaining", -1L); 68 final long millisRemaining = secondsRemaining <= Long.MAX_VALUE / 1000 69 ? secondsRemaining * 1000 70 : Long.MAX_VALUE; 71 final long expiryTimeMs = secondsRemaining == -1L ? -1L : 72 refreshTimeMs + Math.min(Long.MAX_VALUE - refreshTimeMs, millisRemaining); 73 return new CaptivePortalDataShimImpl(new CaptivePortalData.Builder() 74 .setRefreshTime(refreshTimeMs) 75 // captive is mandatory; throws JSONException if absent 76 .setCaptive(obj.getBoolean("captive")) 77 .setUserPortalUrl(getUriOrNull(obj, "user-portal-url")) 78 .setVenueInfoUrl(getUriOrNull(obj, "venue-info-url")) 79 .setBytesRemaining(getLongOrDefault(obj, "bytes-remaining", -1L)) 80 .setExpiryTime(expiryTimeMs) 81 .build()); 82 } 83 84 @RequiresApi(Build.VERSION_CODES.Q) 85 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) isSupported()86 public static boolean isSupported() { 87 return ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q); 88 } 89 getLongOrDefault(JSONObject o, String key, long def)90 private static long getLongOrDefault(JSONObject o, String key, long def) throws JSONException { 91 if (!o.has(key)) return def; 92 return o.getLong(key); 93 } 94 getUriOrNull(JSONObject o, String key)95 private static Uri getUriOrNull(JSONObject o, String key) throws JSONException { 96 if (!o.has(key)) return null; 97 return Uri.parse(o.getString(key)); 98 } 99 100 @Override isCaptive()101 public boolean isCaptive() { 102 return mData.isCaptive(); 103 } 104 105 @Override getByteLimit()106 public long getByteLimit() { 107 return mData.getByteLimit(); 108 } 109 110 @Override getExpiryTimeMillis()111 public long getExpiryTimeMillis() { 112 return mData.getExpiryTimeMillis(); 113 } 114 115 @Override getUserPortalUrl()116 public Uri getUserPortalUrl() { 117 return mData.getUserPortalUrl(); 118 } 119 120 @Override getVenueInfoUrl()121 public Uri getVenueInfoUrl() { 122 return mData.getVenueInfoUrl(); 123 } 124 125 @Override notifyChanged(INetworkMonitorCallbacks cb)126 public void notifyChanged(INetworkMonitorCallbacks cb) throws RemoteException { 127 cb.notifyCaptivePortalDataChanged(mData); 128 } 129 130 /** 131 * Generate a {@link CaptivePortalDataShim} object with a friendly name set 132 * 133 * @param friendlyName The friendly name to set 134 * @return a {@link CaptivePortalDataShim} object with a friendly name set 135 */ 136 @Override withVenueFriendlyName(String friendlyName)137 public CaptivePortalDataShim withVenueFriendlyName(String friendlyName) 138 throws UnsupportedApiLevelException { 139 // Not supported in API level 30 140 throw new UnsupportedApiLevelException("FriendlyName not supported on API 30"); 141 } 142 143 /** 144 * Generate a {@link CaptivePortalDataShim} object with a friendly name and Passpoint external 145 * URLs set 146 * 147 * @param friendlyName The friendly name to set 148 * @param venueInfoUrl Venue information URL 149 * @param termsAndConditionsUrl Terms and conditions URL 150 * 151 * @return a {@link CaptivePortalDataShim} object with friendly name, venue info URL and terms 152 * and conditions URL set 153 */ 154 @Override withPasspointInfo(@onNull String friendlyName, @NonNull Uri venueInfoUrl, @NonNull Uri termsAndConditionsUrl)155 public CaptivePortalDataShim withPasspointInfo(@NonNull String friendlyName, 156 @NonNull Uri venueInfoUrl, @NonNull Uri termsAndConditionsUrl) 157 throws UnsupportedApiLevelException { 158 // Not supported in API level 30 159 throw new UnsupportedApiLevelException("PasspointInfo not supported on API 30"); 160 } 161 } 162