PasspointNetworkEvaluator.java revision e7dfd69fbe21902b89124b99d914ed3c90ba8baf
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 /** 46 * Contained information for a Passpoint network candidate. 47 */ 48 private class PasspointNetworkCandidate { 49 PasspointNetworkCandidate(PasspointProvider provider, PasspointMatch matchStatus, 50 ScanDetail scanDetail) { 51 mProvider = provider; 52 mMatchStatus = matchStatus; 53 mScanDetail = scanDetail; 54 } 55 PasspointProvider mProvider; 56 PasspointMatch mMatchStatus; 57 ScanDetail mScanDetail; 58 } 59 60 public PasspointNetworkEvaluator(PasspointManager passpointManager, 61 WifiConfigManager wifiConfigManager, LocalLog localLog) { 62 mPasspointManager = passpointManager; 63 mWifiConfigManager = wifiConfigManager; 64 mLocalLog = localLog; 65 } 66 67 @Override 68 public String getName() { 69 return NAME; 70 } 71 72 @Override 73 public void update(List<ScanDetail> scanDetails) {} 74 75 @Override 76 public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, 77 WifiConfiguration currentNetwork, String currentBssid, 78 boolean connected, boolean untrustedNetworkAllowed, 79 List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) { 80 // Sweep the ANQP cache to remove any expired ANQP entries. 81 mPasspointManager.sweepCache(); 82 83 // Go through each ScanDetail and find the best provider for each ScanDetail. 84 List<PasspointNetworkCandidate> candidateList = new ArrayList<>(); 85 for (ScanDetail scanDetail : scanDetails) { 86 // Skip non-Passpoint APs. 87 if (!scanDetail.getNetworkDetail().isInterworking()) { 88 continue; 89 } 90 91 // Find the best provider for this ScanDetail. 92 Pair<PasspointProvider, PasspointMatch> bestProvider = 93 mPasspointManager.matchProvider(scanDetail.getScanResult()); 94 if (bestProvider != null) { 95 if (bestProvider.first.isSimCredential() && !mWifiConfigManager.isSimPresent()) { 96 // Skip providers backed by SIM credential when SIM is not present. 97 continue; 98 } 99 candidateList.add(new PasspointNetworkCandidate( 100 bestProvider.first, bestProvider.second, scanDetail)); 101 } 102 } 103 104 // Done if no candidate is found. 105 if (candidateList.isEmpty()) { 106 localLog("No suitable Passpoint network found"); 107 return null; 108 } 109 110 // Find the best Passpoint network among all candidates. 111 PasspointNetworkCandidate bestNetwork = 112 findBestNetwork(candidateList, currentNetwork == null ? null : currentNetwork.SSID); 113 114 // Return the configuration for the current connected network if it is the best network. 115 if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID, 116 ScanResultUtil.createQuotedSSID(bestNetwork.mScanDetail.getSSID()))) { 117 localLog("Staying with current Passpoint network " + currentNetwork.SSID); 118 connectableNetworks.add(Pair.create(bestNetwork.mScanDetail, currentNetwork)); 119 return currentNetwork; 120 } 121 122 WifiConfiguration config = createWifiConfigForProvider(bestNetwork); 123 connectableNetworks.add(Pair.create(bestNetwork.mScanDetail, config)); 124 localLog("Passpoint network to connect to: " + config.SSID); 125 return config; 126 } 127 128 /** 129 * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider. 130 * The newly created WifiConfiguration will also be added to WifiConfigManager. 131 * 132 * @param networkInfo Contained information for the Passpoint network to connect to 133 * @return {@link WifiConfiguration} 134 */ 135 private WifiConfiguration createWifiConfigForProvider(PasspointNetworkCandidate networkInfo) { 136 WifiConfiguration config = networkInfo.mProvider.getWifiConfig(); 137 config.SSID = ScanResultUtil.createQuotedSSID(networkInfo.mScanDetail.getSSID()); 138 if (networkInfo.mMatchStatus == PasspointMatch.HomeProvider) { 139 config.isHomeProviderNetwork = true; 140 } 141 142 // Add the newly created WifiConfiguration to WifiConfigManager. 143 NetworkUpdateResult result = 144 mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); 145 if (!result.isSuccess()) { 146 localLog("Failed to add passpoint network"); 147 return null; 148 } 149 mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID); 150 mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(), 151 networkInfo.mScanDetail.getScanResult(), 0); 152 mWifiConfigManager.updateScanDetailForNetwork( 153 result.getNetworkId(), networkInfo.mScanDetail); 154 return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); 155 } 156 157 /** 158 * Given a list of Passpoint networks (with both provider and scan info), find and return 159 * the one with highest score. The score is calculated using 160 * {@link PasspointNetworkScore#calculateScore}. 161 * 162 * @param networkList List of Passpoint networks 163 * @param currentNetworkSsid The SSID of the currently connected network, null if not connected 164 * @return {@link PasspointNetworkCandidate} 165 */ 166 private PasspointNetworkCandidate findBestNetwork( 167 List<PasspointNetworkCandidate> networkList, String currentNetworkSsid) { 168 PasspointNetworkCandidate bestCandidate = null; 169 int bestScore = Integer.MIN_VALUE; 170 for (PasspointNetworkCandidate candidate : networkList) { 171 ScanDetail scanDetail = candidate.mScanDetail; 172 PasspointMatch match = candidate.mMatchStatus; 173 174 boolean isActiveNetwork = TextUtils.equals(currentNetworkSsid, 175 ScanResultUtil.createQuotedSSID(scanDetail.getSSID())); 176 int score = PasspointNetworkScore.calculateScore(match == PasspointMatch.HomeProvider, 177 scanDetail, mPasspointManager.getANQPElements(scanDetail.getScanResult()), 178 isActiveNetwork); 179 180 if (score > bestScore) { 181 bestCandidate = candidate; 182 bestScore = score; 183 } 184 } 185 localLog("Best Passpoint network " + bestCandidate.mScanDetail.getSSID() + " provided by " 186 + bestCandidate.mProvider.getConfig().getHomeSp().getFqdn()); 187 return bestCandidate; 188 } 189 190 private void localLog(String log) { 191 mLocalLog.log(log); 192 } 193} 194