1/*
2 * Copyright (C) 2016 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
17package com.android.server.wifi.hotspot2;
18
19import android.net.RssiCurve;
20
21import com.android.internal.annotations.VisibleForTesting;
22import com.android.server.wifi.ScanDetail;
23import com.android.server.wifi.hotspot2.anqp.ANQPElement;
24import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
25import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
26import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement;
27
28import java.util.HashMap;
29import java.util.Map;
30
31/**
32 * This is an utility class for calculating score for Passpoint networks.
33 */
34public class PasspointNetworkScore {
35    /**
36     * Award points for network that's a Passpoint home provider.
37     */
38    @VisibleForTesting
39    public static final int HOME_PROVIDER_AWARD = 100;
40
41    /**
42     * Award points for network that provides Internet access.
43     */
44    @VisibleForTesting
45    public static final int INTERNET_ACCESS_AWARD = 50;
46
47    /**
48     * Award points for public or private network.
49     */
50    @VisibleForTesting
51    public static final int PUBLIC_OR_PRIVATE_NETWORK_AWARDS = 4;
52
53    /**
54     * Award points for personal or emergency network.
55     */
56    @VisibleForTesting
57    public static final int PERSONAL_OR_EMERGENCY_NETWORK_AWARDS = 2;
58
59    /**
60     * Award points for network providing restricted or unknown IP address.
61     */
62    @VisibleForTesting
63    public static final int RESTRICTED_OR_UNKNOWN_IP_AWARDS = 1;
64
65    /**
66     * Award points for network providing unrestricted IP address.
67     */
68    @VisibleForTesting
69    public static final int UNRESTRICTED_IP_AWARDS = 2;
70
71    /**
72     * Penalty points for network with WAN port that's down or the load already reached the max.
73     */
74    @VisibleForTesting
75    public static final int WAN_PORT_DOWN_OR_CAPPED_PENALTY = 50;
76
77    // Award points for availability of IPv4 and IPv6 addresses.
78    private static final Map<Integer, Integer> IPV4_SCORES = new HashMap<>();
79    private static final Map<Integer, Integer> IPV6_SCORES = new HashMap<>();
80
81    // Award points based on access network type.
82    private static final Map<NetworkDetail.Ant, Integer> NETWORK_TYPE_SCORES = new HashMap<>();
83
84    /**
85     * Curve for calculating score for RSSI level.
86     */
87    @VisibleForTesting
88    public static final RssiCurve RSSI_SCORE = new RssiCurve(-80 /* start */, 20 /* bucketWidth */,
89            new byte[] {-10, 0, 10, 20, 30, 40} /* rssiBuckets */,
90            20 /* activeNetworkRssiBoost */);
91
92    static {
93        // These are all arbitrarily chosen scores, subject to tuning.
94
95        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.FreePublic, PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
96        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.ChargeablePublic,
97                PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
98        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.PrivateWithGuest,
99                PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
100        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Private,
101                PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
102        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Personal, PERSONAL_OR_EMERGENCY_NETWORK_AWARDS);
103        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.EmergencyOnly,
104                PERSONAL_OR_EMERGENCY_NETWORK_AWARDS);
105        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Wildcard, 0);
106        NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.TestOrExperimental, 0);
107
108        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_NOT_AVAILABLE, 0);
109        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED,
110                RESTRICTED_OR_UNKNOWN_IP_AWARDS);
111        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_SINGLE_NAT,
112                RESTRICTED_OR_UNKNOWN_IP_AWARDS);
113        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_DOUBLE_NAT,
114                RESTRICTED_OR_UNKNOWN_IP_AWARDS);
115        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_UNKNOWN,
116                RESTRICTED_OR_UNKNOWN_IP_AWARDS);
117        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PUBLIC, UNRESTRICTED_IP_AWARDS);
118        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_SINGLE_NAT, UNRESTRICTED_IP_AWARDS);
119        IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_DOUBLE_NAT, UNRESTRICTED_IP_AWARDS);
120
121        IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_NOT_AVAILABLE, 0);
122        IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_UNKNOWN,
123                RESTRICTED_OR_UNKNOWN_IP_AWARDS);
124        IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_AVAILABLE,
125                UNRESTRICTED_IP_AWARDS);
126    }
127
128
129    /**
130     * Calculate and return a score associated with the given Passpoint network.
131     * The score is calculated with the following preferences:
132     * - Prefer home provider
133     * - Prefer network that provides Internet access
134     * - Prefer network with active WAN port with available load
135     * - Prefer network that provides unrestricted IP address
136     * - Prefer currently active network
137     * - Prefer AP with higher RSSI
138     *
139     * This can be expanded for additional preference in the future (e.g. AP station count, link
140     * speed, and etc).
141     *
142     * @param isHomeProvider Flag indicating home provider
143     * @param scanDetail The ScanDetail associated with the AP
144     * @param isActiveNetwork Flag indicating current active network
145     * @return integer score
146     */
147    public static int calculateScore(boolean isHomeProvider, ScanDetail scanDetail,
148            Map<ANQPElementType, ANQPElement> anqpElements, boolean isActiveNetwork) {
149        NetworkDetail networkDetail = scanDetail.getNetworkDetail();
150        int score = 0;
151        if (isHomeProvider) {
152            score += HOME_PROVIDER_AWARD;
153        }
154
155        // Adjust score based on Internet accessibility.
156        score += (networkDetail.isInternet() ? 1 : -1) * INTERNET_ACCESS_AWARD;
157
158        // Adjust score based on the network type.
159        score += NETWORK_TYPE_SCORES.get(networkDetail.getAnt());
160
161        if (anqpElements != null) {
162            HSWanMetricsElement wm =
163                    (HSWanMetricsElement) anqpElements.get(ANQPElementType.HSWANMetrics);
164            if (wm != null) {
165                if (wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isCapped()) {
166                    score -= WAN_PORT_DOWN_OR_CAPPED_PENALTY;
167                }
168            }
169
170            IPAddressTypeAvailabilityElement ipa = (IPAddressTypeAvailabilityElement)
171                    anqpElements.get(ANQPElementType.ANQPIPAddrAvailability);
172
173            if (ipa != null) {
174                Integer v4Score = IPV4_SCORES.get(ipa.getV4Availability());
175                Integer v6Score = IPV6_SCORES.get(ipa.getV6Availability());
176                v4Score = v4Score != null ? v4Score : 0;
177                v6Score = v6Score != null ? v6Score : 0;
178                score += (v4Score + v6Score);
179            }
180        }
181
182        score += RSSI_SCORE.lookupScore(scanDetail.getScanResult().level, isActiveNetwork);
183        return score;
184    }
185}
186