/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.os.Process; import android.telephony.TelephonyManager; import android.util.LocalLog; import com.android.server.wifi.WifiNetworkSelector.NetworkEvaluator; import com.android.server.wifi.util.ScanResultUtil; import com.android.server.wifi.util.TelephonyUtil; import java.util.List; import javax.annotation.concurrent.NotThreadSafe; /** * Evaluator to select a Carrier Wi-Fi network which can be connected to. The evaluator performs * two functions: * * 1. Filtering: figure out which of the networks is a Carrier Wi-Fi network (using the * {@link CarrierNetworkConfig} APIs). * 2. Evaluation: current evaluator API has 2 outputs (effectively): * - Connectable networks: all networks which match #1 will be fed to this API * - Selected network: a single network 'selected' by the evaluator. A simple max(RSSI) will be * used to pick one network from among the connectable networks. * * Note: This class is not thread safe and meant to be used only from {@link WifiNetworkSelector}. */ @NotThreadSafe public class CarrierNetworkEvaluator implements NetworkEvaluator { private static final String TAG = "CarrierNetworkEvaluator"; private final WifiConfigManager mWifiConfigManager; private final CarrierNetworkConfig mCarrierNetworkConfig; private final LocalLog mLocalLog; private final WifiInjector mWifiInjector; private TelephonyManager mTelephonyManager; public CarrierNetworkEvaluator(WifiConfigManager wifiConfigManager, CarrierNetworkConfig carrierNetworkConfig, LocalLog localLog, WifiInjector wifiInjector) { mWifiConfigManager = wifiConfigManager; mCarrierNetworkConfig = carrierNetworkConfig; mLocalLog = localLog; mWifiInjector = wifiInjector; } private TelephonyManager getTelephonyManager() { if (mTelephonyManager == null) { mTelephonyManager = mWifiInjector.makeTelephonyManager(); } return mTelephonyManager; } @Override public @EvaluatorId int getId() { return EVALUATOR_ID_CARRIER; } @Override public String getName() { return TAG; } @Override public void update(List scanDetails) { // nothing to be done } @Override public WifiConfiguration evaluateNetworks(List scanDetails, WifiConfiguration currentNetwork, String currentBssid, boolean connected, boolean untrustedNetworkAllowed, OnConnectableListener onConnectableListener) { if (!mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) { return null; } int currentMaxRssi = Integer.MIN_VALUE; WifiConfiguration configWithMaxRssi = null; for (ScanDetail scanDetail : scanDetails) { ScanResult scanResult = scanDetail.getScanResult(); if (!ScanResultUtil.isScanResultForEapNetwork(scanResult) || !mCarrierNetworkConfig.isCarrierNetwork(scanResult.SSID)) { continue; } int eapType = mCarrierNetworkConfig.getNetworkEapType(scanResult.SSID); if (!TelephonyUtil.isSimEapMethod(eapType)) { mLocalLog.log(TAG + ": eapType is not a carrier eap method: " + eapType); continue; } // If the user previously forgot this network, don't select it. if (mWifiConfigManager.wasEphemeralNetworkDeleted( ScanResultUtil.createQuotedSSID(scanResult.SSID))) { mLocalLog.log(TAG + ": Ignoring disabled ephemeral SSID: " + WifiNetworkSelector.toScanId(scanResult)); continue; } WifiConfiguration config = ScanResultUtil.createNetworkFromScanResult(scanResult); config.ephemeral = true; if (config.enterpriseConfig == null) { config.enterpriseConfig = new WifiEnterpriseConfig(); } config.enterpriseConfig.setEapMethod(eapType); // Check if we already have a network with the same credentials in WifiConfigManager // database. If yes, we should check if the network is currently blacklisted. WifiConfiguration existingNetwork = mWifiConfigManager.getConfiguredNetwork(config.configKey()); if (existingNetwork != null && !existingNetwork.getNetworkSelectionStatus().isNetworkEnabled() && !mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId)) { mLocalLog.log(TAG + ": Ignoring blacklisted network: " + WifiNetworkSelector.toNetworkString(existingNetwork)); continue; } // Add the newly created WifiConfiguration to WifiConfigManager. NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); if (!result.isSuccess()) { mLocalLog.log(TAG + ": Failed to add carrier network: " + config); continue; } if (!mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID)) { mLocalLog.log(TAG + ": Failed to enable carrier network: " + config); continue; } if (!mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(), scanResult, 0)) { mLocalLog.log( TAG + ": Failed to set network candidate for carrier network: " + config); continue; } config = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); WifiConfiguration.NetworkSelectionStatus nss = null; if (config != null) { nss = config.getNetworkSelectionStatus(); } if (nss == null) { mLocalLog.log(TAG + ": null network selection status for: " + config); continue; } if (nss.getCandidate() != null && nss.getCandidate().level < scanResult.level) { mWifiConfigManager.updateScanDetailForNetwork(result.getNetworkId(), scanDetail); } onConnectableListener.onConnectable(scanDetail, config, 0); if (scanResult.level > currentMaxRssi) { configWithMaxRssi = config; currentMaxRssi = scanResult.level; } } return configWithMaxRssi; } }