PasspointNetworkEvaluator.java revision 50bc1e851d073d4a986f5b32072f94bbaba86a95
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.wifi.WifiConfiguration;
20import android.os.Process;
21import android.text.TextUtils;
22import android.util.LocalLog;
23import android.util.Pair;
24
25import com.android.server.wifi.NetworkUpdateResult;
26import com.android.server.wifi.ScanDetail;
27import com.android.server.wifi.WifiConfigManager;
28import com.android.server.wifi.WifiNetworkSelector;
29import com.android.server.wifi.util.ScanResultUtil;
30
31import java.util.ArrayList;
32import java.util.List;
33
34/**
35 * This class is the WifiNetworkSelector.NetworkEvaluator implementation for
36 * Passpoint networks.
37 */
38public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
39    private static final String NAME = "PasspointNetworkEvaluator";
40
41    private final PasspointManager mPasspointManager;
42    private final WifiConfigManager mWifiConfigManager;
43    private final LocalLog mLocalLog;
44
45    public PasspointNetworkEvaluator(PasspointManager passpointManager,
46            WifiConfigManager wifiConfigManager, LocalLog localLog) {
47        mPasspointManager = passpointManager;
48        mWifiConfigManager = wifiConfigManager;
49        mLocalLog = localLog;
50    }
51
52    @Override
53    public String getName() {
54        return NAME;
55    }
56
57    @Override
58    public void update(List<ScanDetail> scanDetails) {}
59
60    @Override
61    public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
62                    WifiConfiguration currentNetwork, String currentBssid,
63                    boolean connected, boolean untrustedNetworkAllowed,
64                    List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
65        // Sweep the ANQP cache to remove any expired ANQP entries.
66        mPasspointManager.sweepCache();
67
68        // Go through each ScanDetail and find the best provider for each ScanDetail.
69        List<Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>>> providerList =
70                new ArrayList<>();
71        for (ScanDetail scanDetail : scanDetails) {
72            // Skip non-Passpoint APs.
73            if (!scanDetail.getNetworkDetail().isInterworking()) {
74                continue;
75            }
76
77            // Find the best provider for this ScanDetail.
78            Pair<PasspointProvider, PasspointMatch> bestProvider =
79                    mPasspointManager.matchProvider(scanDetail.getScanResult());
80            if (bestProvider != null) {
81                providerList.add(Pair.create(scanDetail, bestProvider));
82            }
83        }
84
85        // Done if no matching provider is found.
86        if (providerList.isEmpty()) {
87            localLog("No suitable Passpoint network found");
88            return null;
89        }
90
91        // Find the best Passpoint network among all matches.
92        Pair<PasspointProvider, ScanDetail> bestNetwork = findBestNetwork(providerList,
93                currentNetwork == null ? null : currentNetwork.SSID);
94
95        // Return the configuration for the current connected network if it is the best network.
96        if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID,
97                ScanResultUtil.createQuotedSSID(bestNetwork.second.getSSID()))) {
98            localLog("Staying with current Passpoint network " + currentNetwork.SSID);
99            connectableNetworks.add(Pair.create(bestNetwork.second, currentNetwork));
100            return currentNetwork;
101        }
102
103        WifiConfiguration config =
104                createWifiConfigForProvider(bestNetwork.first, bestNetwork.second);
105        connectableNetworks.add(Pair.create(bestNetwork.second, config));
106        localLog("Passpoint network to connect to: " + config.SSID);
107        return config;
108    }
109
110    /**
111     * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider.
112     * The newly created WifiConfiguration will also be added to WifiConfigManager.
113     *
114     * @param provider The provider to create WifiConfiguration from
115     * @param scanDetail The ScanDetail to create WifiConfiguration from
116     * @return {@link WifiConfiguration}
117     */
118    private WifiConfiguration createWifiConfigForProvider(PasspointProvider provider,
119            ScanDetail scanDetail) {
120        WifiConfiguration config = provider.getWifiConfig();
121        config.SSID = ScanResultUtil.createQuotedSSID(scanDetail.getSSID());
122
123        // Add the newly created WifiConfiguration to WifiConfigManager.
124        NetworkUpdateResult result =
125                mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
126        if (!result.isSuccess()) {
127            localLog("Failed to add passpoint network");
128            return null;
129        }
130        mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID);
131        mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(),
132                scanDetail.getScanResult(), 0);
133        mWifiConfigManager.updateScanDetailForNetwork(result.getNetworkId(), scanDetail);
134        return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
135    }
136
137    /**
138     * Given a list of Passpoint networks (with both provider and scan info), find and return
139     * the one with highest score.  The score is calculated using
140     * {@link PasspointNetworkScore#calculateScore}.
141     *
142     * @param networkList List of Passpoint networks
143     * @param currentNetworkSsid The SSID of the currently connected network, null if not connected
144     * @return {@link PasspointProvider} and {@link ScanDetail} associated with the network
145     */
146    private Pair<PasspointProvider, ScanDetail> findBestNetwork(
147            List<Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>>> networkList,
148            String currentNetworkSsid) {
149        ScanDetail bestScanDetail = null;
150        PasspointProvider bestProvider = null;
151        int bestScore = Integer.MIN_VALUE;
152        for (Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>> candidate : networkList) {
153            ScanDetail scanDetail = candidate.first;
154            PasspointProvider provider = candidate.second.first;
155            PasspointMatch match = candidate.second.second;
156
157            boolean isActiveNetwork = TextUtils.equals(currentNetworkSsid,
158                    ScanResultUtil.createQuotedSSID(scanDetail.getSSID()));
159            int score = PasspointNetworkScore.calculateScore(match == PasspointMatch.HomeProvider,
160                    scanDetail, mPasspointManager.getANQPElements(scanDetail.getScanResult()),
161                    isActiveNetwork);
162
163            if (score > bestScore) {
164                bestScanDetail = scanDetail;
165                bestProvider = provider;
166                bestScore = score;
167            }
168        }
169        localLog("Best Passpoint network " + bestScanDetail.getSSID() + " provided by "
170                + bestProvider.getConfig().getHomeSp().getFqdn());
171        return Pair.create(bestProvider, bestScanDetail);
172    }
173
174    private void localLog(String log) {
175        if (mLocalLog != null) {
176            mLocalLog.log(log);
177        }
178    }
179}
180