1 /*
2  * Copyright (C) 2021 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.tv.settings.library.network;
18 
19 import android.annotation.IntDef;
20 import android.annotation.MainThread;
21 import android.annotation.TargetApi;
22 import android.net.wifi.ScanResult;
23 import android.net.wifi.WifiConfiguration;
24 import android.net.wifi.WifiInfo;
25 import android.net.wifi.WifiManager;
26 import android.os.Build;
27 import android.text.TextUtils;
28 
29 import androidx.annotation.NonNull;
30 
31 import com.android.wifitrackerlib.PasspointWifiEntry;
32 import com.android.wifitrackerlib.WifiEntry;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 @TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
40 public class AccessPoint implements Comparable<AccessPoint> {
41     public static final int SECURITY_NONE = WifiInfo.SECURITY_TYPE_OPEN;
42     public static final int SECURITY_WEP = WifiInfo.SECURITY_TYPE_WEP;
43     public static final int SECURITY_PSK = WifiInfo.SECURITY_TYPE_PSK;
44     public static final int SECURITY_EAP = WifiInfo.SECURITY_TYPE_EAP;
45     public static final int SECURITY_OWE = WifiInfo.SECURITY_TYPE_OWE;
46     public static final int SECURITY_SAE = WifiInfo.SECURITY_TYPE_SAE;
47     public static final int SECURITY_EAP_SUITE_B =
48             WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT;
49 
50     public static final String KEY_PREFIX_AP = "AP:";
51 
52     @IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST})
53     @Retention(RetentionPolicy.SOURCE)
54     public @interface Speed {
55         /**
56          * Constant value representing an unlabeled / unscored network.
57          */
58         int NONE = 0;
59         /**
60          * Constant value representing a slow speed network connection.
61          */
62         int SLOW = 5;
63         /**
64          * Constant value representing a medium speed network connection.
65          */
66         int MODERATE = 10;
67         /**
68          * Constant value representing a fast speed network connection.
69          */
70         int FAST = 20;
71         /**
72          * Constant value representing a very fast speed network connection.
73          */
74         int VERY_FAST = 30;
75     }
76 
77     private final WifiEntry mWifiEntry;
78 
79     private final WifiEntry.WifiEntryCallback mEntryCallback = new WifiEntry.WifiEntryCallback() {
80         @Override
81         public void onUpdated() {
82             if (mAccessPointListener != null) {
83                 mAccessPointListener.onAccessPointChanged(AccessPoint.this);
84                 mAccessPointListener.onLevelChanged(AccessPoint.this);
85             }
86         }
87     };
88 
89     AccessPoint.AccessPointListener mAccessPointListener;
90 
AccessPoint(WifiEntry wifiEntry)91     public AccessPoint(WifiEntry wifiEntry) {
92         mWifiEntry = wifiEntry;
93     }
94 
95     /*
96      * Use this for any new code that was not written using legacy WifiTracker AccessPoint
97      * interface.
98      */
getWifiEntry()99     public WifiEntry getWifiEntry() {
100         return mWifiEntry;
101     }
102 
103     @Override
compareTo(@onNull AccessPoint other)104     public int compareTo(@NonNull AccessPoint other) {
105         return WifiEntry.WIFI_PICKER_COMPARATOR.compare(mWifiEntry, other.mWifiEntry);
106     }
107 
108     @Override
equals(Object other)109     public boolean equals(Object other) {
110         if (!(other instanceof AccessPoint)) return false;
111         return (this.compareTo((AccessPoint) other) == 0);
112     }
113 
114     @Override
hashCode()115     public int hashCode() {
116         return mWifiEntry.hashCode();
117     }
118 
119     @Override
toString()120     public String toString() {
121         return mWifiEntry.toString();
122     }
123 
124     /**
125      * Generates an AccessPoint key for a given scan result
126      *
127      * @param result Scan result
128      * @return AccessPoint key
129      */
getKey(ScanResult result)130     public static String getKey(ScanResult result) {
131         return getKey(result.SSID, result.BSSID, getSecurity(result));
132     }
133 
134     /**
135      * Returns the AccessPoint key for a normal non-Passpoint network by ssid/bssid and security.
136      */
getKey(String ssid, String bssid, int security)137     private static String getKey(String ssid, String bssid, int security) {
138         StringBuilder builder = new StringBuilder();
139         builder.append(KEY_PREFIX_AP);
140         if (TextUtils.isEmpty(ssid)) {
141             builder.append(bssid);
142         } else {
143             builder.append(ssid);
144         }
145         builder.append(',').append(security);
146         return builder.toString();
147     }
148 
getKey()149     public String getKey() {
150         return getKey(getSsidStr(), mWifiEntry.getMacAddress(), getSecurity());
151     }
152 
getConfig()153     public WifiConfiguration getConfig() {
154         return mWifiEntry.getWifiConfiguration();
155     }
156 
getInfo()157     public WifiInfo getInfo() {
158         return null;
159     }
160 
161     /**
162      * Returns the number of levels to show for a Wifi icon, from 0 to
163      * {@link WifiManager#getMaxSignalLevel()}.
164      */
getLevel()165     public int getLevel() {
166         return mWifiEntry.getLevel();
167     }
168 
169     /**
170      * Returns if the network should be considered metered.
171      */
isMetered()172     public boolean isMetered() {
173         return mWifiEntry.isMetered();
174     }
175 
getSecurity(WifiEntry wifiEntry)176     public static int getSecurity(WifiEntry wifiEntry) {
177         return getSingleSecurityTypeFromMultipleSecurityTypes(wifiEntry.getSecurityTypes());
178     }
179 
180     /**
181      * Returns a single WifiInfo security type from the list of multiple WifiInfo security
182      * types supported by an entry.
183      *
184      * Single security types will have a 1-to-1 mapping.
185      * Multiple security type networks will collapse to the lowest security type in the group:
186      *     - Open/OWE -> Open
187      *     - PSK/SAE -> PSK
188      *     - EAP/EAP-WPA3 -> EAP
189      * This mapping is copied from {@link WifiEntry} to avoid unintentional changes to TVSettings
190      * behavior when connecting to a given network.
191      */
getSingleSecurityTypeFromMultipleSecurityTypes( @onNull List<Integer> securityTypes)192     private static int getSingleSecurityTypeFromMultipleSecurityTypes(
193             @NonNull List<Integer> securityTypes) {
194         if (securityTypes.isEmpty()) {
195             return WifiInfo.SECURITY_TYPE_UNKNOWN;
196         }
197 
198         if (securityTypes.size() == 2) {
199             if (securityTypes.contains(WifiInfo.SECURITY_TYPE_OPEN)) {
200                 return WifiInfo.SECURITY_TYPE_OPEN;
201             }
202             if (securityTypes.contains(WifiInfo.SECURITY_TYPE_PSK)) {
203                 return WifiInfo.SECURITY_TYPE_PSK;
204             }
205             if (securityTypes.contains(WifiInfo.SECURITY_TYPE_EAP)) {
206                 return WifiInfo.SECURITY_TYPE_EAP;
207             }
208         }
209 
210         // Default to the first security type if we don't need any special mapping.
211         return securityTypes.get(0);
212     }
213 
getSecurity()214     public int getSecurity() {
215         return getSecurity(mWifiEntry);
216     }
217 
getSsidStr()218     public String getSsidStr() {
219         return mWifiEntry.getSsid();
220     }
221 
getSsid()222     public CharSequence getSsid() {
223         return getSsidStr();
224     }
225 
226     /**
227      * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
228      */
getTitle()229     public String getTitle() {
230         return mWifiEntry.getTitle();
231     }
232 
getSettingsSummary()233     public String getSettingsSummary() {
234         return "";
235     }
236 
isActive()237     public boolean isActive() {
238         return mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED;
239     }
240 
241     /**
242      * Return true if this AccessPoint represents a Passpoint AP.
243      */
isPasspoint()244     public boolean isPasspoint() {
245         return mWifiEntry instanceof PasspointWifiEntry;
246     }
247 
isSaved()248     public boolean isSaved() {
249         return mWifiEntry.isSaved();
250     }
251 
setListener(AccessPoint.AccessPointListener listener)252     public void setListener(AccessPoint.AccessPointListener listener) {
253         mAccessPointListener = listener;
254         mWifiEntry.setListener(listener != null ? mEntryCallback : null);
255     }
256 
convertToQuotedString(String string)257     public static String convertToQuotedString(String string) {
258         return "\"" + string + "\"";
259     }
260 
getSecurity(ScanResult result)261     private static int getSecurity(ScanResult result) {
262         List<Integer> securityTypes = new ArrayList<>();
263         for (int securityType : result.getSecurityTypes()) {
264             securityTypes.add(securityType);
265         }
266         return getSingleSecurityTypeFromMultipleSecurityTypes(securityTypes);
267     }
268 
269     /**
270      * Callbacks relaying changes to the AccessPoint representation.
271      *
272      * <p>All methods are invoked on the Main Thread.
273      */
274     public interface AccessPointListener {
275 
276         /**
277          * Indicates a change to the externally visible state of the AccessPoint trigger by an
278          * update of ScanResults, saved configuration state, connection state, or score
279          * (labels/metered) state.
280          *
281          * <p>Clients should refresh their view of the AccessPoint to match the updated state when
282          * this is invoked. Overall this method is extraneous if clients are listening to
283          * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
284          *
285          * <p>Examples of changes include signal strength, connection state, speed label, and
286          * generally anything that would impact the summary string.
287          *
288          * @param accessPoint The accessPoint object the listener was registered on which has
289          *                    changed
290          */
291         @MainThread
onAccessPointChanged(AccessPoint accessPoint)292         void onAccessPointChanged(AccessPoint accessPoint);
293 
294         /**
295          * Indicates the "wifi pie signal level" has changed, retrieved via calls to
296          * {@link AccessPoint#getLevel()}.
297          *
298          * <p>This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also
299          * extraneous if the client is already reacting to that or the
300          * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
301          *
302          * @param accessPoint The accessPoint object the listener was registered on whose level has
303          *                    changed
304          */
305         @MainThread
onLevelChanged(AccessPoint accessPoint)306         void onLevelChanged(AccessPoint accessPoint);
307     }
308 }
309