PasspointNetworkEvaluator.java revision b54f07e01c9daef8883c85e09f61436f2c06cc72
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            return null;
88        }
89
90        // Find the best Passpoint network among all matches.
91        Pair<PasspointProvider, ScanDetail> bestNetwork = findBestNetwork(providerList,
92                currentNetwork == null ? null : currentNetwork.SSID);
93
94        // Return the configuration for the current connected network if it is the best network.
95        if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID,
96                ScanResultUtil.createQuotedSSID(bestNetwork.second.getSSID()))) {
97            connectableNetworks.add(Pair.create(bestNetwork.second, currentNetwork));
98            return currentNetwork;
99        }
100
101        WifiConfiguration config =
102                createWifiConfigForProvider(bestNetwork.first, bestNetwork.second);
103        connectableNetworks.add(Pair.create(bestNetwork.second, config));
104        return config;
105    }
106
107    /**
108     * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider.
109     * The newly created WifiConfiguration will also be added to WifiConfigManager.
110     *
111     * @param provider The provider to create WifiConfiguration from
112     * @param scanDetail The ScanDetail to create WifiConfiguration from
113     * @return {@link WifiConfiguration}
114     */
115    private WifiConfiguration createWifiConfigForProvider(PasspointProvider provider,
116            ScanDetail scanDetail) {
117        WifiConfiguration config = provider.getWifiConfig();
118        config.SSID = ScanResultUtil.createQuotedSSID(scanDetail.getSSID());
119
120        // Add the newly created WifiConfiguration to WifiConfigManager.
121        NetworkUpdateResult result =
122                mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
123        if (!result.isSuccess()) {
124            localLog("Failed to add passpoint network");
125            return null;
126        }
127        mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID);
128        mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(),
129                scanDetail.getScanResult(), 0);
130        mWifiConfigManager.updateScanDetailForNetwork(result.getNetworkId(), scanDetail);
131        return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
132    }
133
134    /**
135     * Given a list of Passpoint networks (with both provider and scan info), find and return
136     * the one with highest score.  The score is calculated using
137     * {@link PasspointNetworkScore#calculateScore}.
138     *
139     * @param networkList List of Passpoint networks
140     * @param currentNetworkSsid The SSID of the currently connected network, null if not connected
141     * @return {@link PasspointProvider} and {@link ScanDetail} associated with the network
142     */
143    private Pair<PasspointProvider, ScanDetail> findBestNetwork(
144            List<Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>>> networkList,
145            String currentNetworkSsid) {
146        ScanDetail bestScanDetail = null;
147        PasspointProvider bestProvider = null;
148        int bestScore = Integer.MIN_VALUE;
149        for (Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>> candidate : networkList) {
150            ScanDetail scanDetail = candidate.first;
151            PasspointProvider provider = candidate.second.first;
152            PasspointMatch match = candidate.second.second;
153
154            boolean isActiveNetwork = TextUtils.equals(currentNetworkSsid,
155                    ScanResultUtil.createQuotedSSID(scanDetail.getSSID()));
156            int score = PasspointNetworkScore.calculateScore(match == PasspointMatch.HomeProvider,
157                    scanDetail, mPasspointManager.getANQPElements(scanDetail.getScanResult()),
158                    isActiveNetwork);
159
160            if (score > bestScore) {
161                bestScanDetail = scanDetail;
162                bestProvider = provider;
163                bestScore = score;
164            }
165        }
166        return Pair.create(bestProvider, bestScanDetail);
167    }
168
169    private void localLog(String log) {
170        if (mLocalLog != null) {
171            mLocalLog.log(log);
172        }
173    }
174}
175