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