diff options
33 files changed, 831 insertions, 275 deletions
diff --git a/libwifi_system/hostapd_manager.cpp b/libwifi_system/hostapd_manager.cpp index 13bacae22..68184e901 100644 --- a/libwifi_system/hostapd_manager.cpp +++ b/libwifi_system/hostapd_manager.cpp @@ -109,6 +109,14 @@ bool HostapdManager::WriteHostapdConfig(const string& config) { int error = errno; LOG(ERROR) << "Cannot write hostapd config to \"" << kHostapdConfigFilePath << "\": " << strerror(error); + struct stat st; + int result = stat(kHostapdConfigFilePath, &st); + if (result == 0) { + LOG(ERROR) << "hostapd config file uid: "<< st.st_uid << ", gid: " << st.st_gid + << ", mode: " << st.st_mode; + } else { + LOG(ERROR) << "Error calling stat() on hostapd config file: " << strerror(errno); + } return false; } return true; diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java index 2c3e5f7e0..ba114df8a 100644 --- a/service/java/com/android/server/wifi/FrameworkFacade.java +++ b/service/java/com/android/server/wifi/FrameworkFacade.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.app.ActivityManager; import android.app.AppGlobals; import android.app.PendingIntent; import android.content.Context; @@ -161,4 +162,14 @@ public class FrameworkFacade { public boolean inStorageManagerCryptKeeperBounce() { return StorageManager.inCryptKeeperBounce(); } + + /** + * Check if the provided uid is the app in the foreground. + * @param uid the uid to check + * @return true if the app is in the foreground, false otherwise + * @throws RemoteException + */ + public boolean isAppForeground(int uid) throws RemoteException { + return ActivityManager.getService().isAppForeground(uid); + } } diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java index bf79edaf8..9ac70689b 100644 --- a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java @@ -107,9 +107,6 @@ public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluat continue; } - WifiConfiguration.NetworkSelectionStatus status = - network.getNetworkSelectionStatus(); - // If a configuration is temporarily disabled, re-enable it before trying // to connect to it. mWifiConfigManager.tryEnableNetwork(network.networkId); @@ -120,28 +117,26 @@ public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluat // Clear the cached candidate, score and seen. mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId); - boolean networkDisabled = false; - boolean networkStringLogged = false; - for (int index = WifiConfiguration.NetworkSelectionStatus - .NETWORK_SELECTION_DISABLED_STARTING_INDEX; - index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; - index++) { - int count = status.getDisableReasonCounter(index); - if (count > 0) { - networkDisabled = true; - if (!networkStringLogged) { - sbuf.append(" ").append(WifiNetworkSelector.toNetworkString(network)) - .append(" "); - networkStringLogged = true; + // Log disabled network. + WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus(); + if (!status.isNetworkEnabled()) { + sbuf.append(" ").append(WifiNetworkSelector.toNetworkString(network)).append(" "); + for (int index = WifiConfiguration.NetworkSelectionStatus + .NETWORK_SELECTION_DISABLED_STARTING_INDEX; + index < WifiConfiguration.NetworkSelectionStatus + .NETWORK_SELECTION_DISABLED_MAX; + index++) { + int count = status.getDisableReasonCounter(index); + // Here we log the reason as long as its count is greater than zero. The + // network may not be disabled because of this particular reason. Logging + // this information anyway to help understand what happened to the network. + if (count > 0) { + sbuf.append("reason=") + .append(WifiConfiguration.NetworkSelectionStatus + .getNetworkDisableReasonString(index)) + .append(", count=").append(count).append("; "); } - sbuf.append("reason=") - .append(WifiConfiguration.NetworkSelectionStatus - .getNetworkDisableReasonString(index)) - .append(", count=").append(count).append("; "); } - } - - if (networkDisabled) { sbuf.append("\n"); } } @@ -264,11 +259,12 @@ public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluat for (WifiConfiguration network : associatedConfigurations) { /** - * Ignore Passpoint networks. Passpoint networks are also considered as "saved" - * network, but without being persisted to the storage. They are being evaluated - * by {@link PasspointNetworkEvaluator}. + * Ignore Passpoint and Ephemeral networks. They are configured networks, + * but without being persisted to the storage. They are evaluated by + * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator} + * respectively. */ - if (network.isPasspoint()) { + if (network.isPasspoint() || network.isEphemeral()) { continue; } diff --git a/service/java/com/android/server/wifi/SoftApModeConfiguration.java b/service/java/com/android/server/wifi/SoftApModeConfiguration.java new file mode 100644 index 000000000..e8805602e --- /dev/null +++ b/service/java/com/android/server/wifi/SoftApModeConfiguration.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 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.WifiConfiguration; + +/** + * Object holding the parameters needed to start SoftAp mode. + * + * Initially, this will hold the WifiConfiguration and mode. + */ +public class SoftApModeConfiguration { + final int mTargetMode; + final WifiConfiguration mConfig; + + SoftApModeConfiguration(int targetMode, WifiConfiguration config) { + mTargetMode = targetMode; + mConfig = config; + } + + public int getTargetMode() { + return mTargetMode; + } + + public WifiConfiguration getWifiConfiguration() { + return mConfig; + } +} diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java index 99067430b..e9c20db32 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java @@ -26,6 +26,7 @@ import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HS import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSOSUProviders; import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSWANMetrics; +import android.annotation.NonNull; import android.content.Context; import android.hardware.wifi.supplicant.V1_0.ISupplicant; import android.hardware.wifi.supplicant.V1_0.ISupplicantIface; @@ -49,6 +50,7 @@ import android.os.HwRemoteBinder; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import com.android.server.wifi.hotspot2.AnqpEvent; @@ -125,10 +127,8 @@ public class SupplicantStaIfaceHal { }; private String mIfaceName; - // Currently configured network in wpa_supplicant - private SupplicantStaNetworkHal mCurrentNetwork; - // Currently configured network's framework network Id. - private int mFrameworkNetworkId = WifiConfiguration.INVALID_NETWORK_ID; + private SupplicantStaNetworkHal mCurrentNetworkRemoteHandle; + private WifiConfiguration mCurrentNetworkLocalConfig; private final Context mContext; private final WifiMonitor mWifiMonitor; @@ -254,6 +254,13 @@ public class SupplicantStaIfaceHal { return true; } + private int getCurrentNetworkId() { + if (mCurrentNetworkLocalConfig == null) { + return WifiConfiguration.INVALID_NETWORK_ID; + } + return mCurrentNetworkLocalConfig.networkId; + } + private boolean initSupplicantStaIface() { synchronized (mLock) { /** List all supplicant Ifaces */ @@ -353,9 +360,11 @@ public class SupplicantStaIfaceHal { * Add a network configuration to wpa_supplicant. * * @param config Config corresponding to the network. - * @return SupplicantStaNetwork of the added network in wpa_supplicant. + * @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects + * for the current network. */ - private SupplicantStaNetworkHal addNetworkAndSaveConfig(WifiConfiguration config) { + private Pair<SupplicantStaNetworkHal, WifiConfiguration> + addNetworkAndSaveConfig(WifiConfiguration config) { logi("addSupplicantStaNetwork via HIDL"); if (config == null) { loge("Cannot add NULL network!"); @@ -379,39 +388,43 @@ public class SupplicantStaIfaceHal { } return null; } - return network; + return new Pair(network, new WifiConfiguration(config)); } /** * Add the provided network configuration to wpa_supplicant and initiate connection to it. * This method does the following: - * 1. Triggers disconnect command to wpa_supplicant (if |shouldDisconnect| is true). - * 2. Remove any existing network in wpa_supplicant. - * 3. Add a new network to wpa_supplicant. - * 4. Save the provided configuration to wpa_supplicant. - * 5. Select the new network in wpa_supplicant. + * 1. If |config| is different to the current supplicant network, removes all supplicant + * networks and saves |config|. + * 2. Select the new network in wpa_supplicant. * * @param config WifiConfiguration parameters for the provided network. * @return {@code true} if it succeeds, {@code false} otherwise */ - public boolean connectToNetwork(WifiConfiguration config) { - mFrameworkNetworkId = WifiConfiguration.INVALID_NETWORK_ID; - mCurrentNetwork = null; + public boolean connectToNetwork(@NonNull WifiConfiguration config) { logd("connectToNetwork " + config.configKey()); - if (!removeAllNetworks()) { - loge("Failed to remove existing networks"); - return false; - } - mCurrentNetwork = addNetworkAndSaveConfig(config); - if (mCurrentNetwork == null) { - loge("Failed to add/save network configuration: " + config.configKey()); - return false; + if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) { + logd("Network is already saved, will not trigger remove and add operation."); + } else { + mCurrentNetworkRemoteHandle = null; + mCurrentNetworkLocalConfig = null; + if (!removeAllNetworks()) { + loge("Failed to remove existing networks"); + return false; + } + Pair<SupplicantStaNetworkHal, WifiConfiguration> pair = addNetworkAndSaveConfig(config); + if (pair == null) { + loge("Failed to add/save network configuration: " + config.configKey()); + return false; + } + mCurrentNetworkRemoteHandle = pair.first; + mCurrentNetworkLocalConfig = pair.second; } - if (!mCurrentNetwork.select()) { + + if (!mCurrentNetworkRemoteHandle.select()) { loge("Failed to select network configuration: " + config.configKey()); return false; } - mFrameworkNetworkId = config.networkId; return true; } @@ -428,14 +441,14 @@ public class SupplicantStaIfaceHal { * @return {@code true} if it succeeds, {@code false} otherwise */ public boolean roamToNetwork(WifiConfiguration config) { - if (mFrameworkNetworkId != config.networkId || mCurrentNetwork == null) { + if (getCurrentNetworkId() != config.networkId) { Log.w(TAG, "Cannot roam to a different network, initiate new connection. " - + "Current network ID: " + mFrameworkNetworkId); + + "Current network ID: " + getCurrentNetworkId()); return connectToNetwork(config); } String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID(); logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")"); - if (!mCurrentNetwork.setBssid(bssid)) { + if (!mCurrentNetworkRemoteHandle.setBssid(bssid)) { loge("Failed to set new bssid on network: " + config.configKey()); return false; } @@ -498,6 +511,21 @@ public class SupplicantStaIfaceHal { } /** + * Remove the request |networkId| from supplicant if it's the current network, + * if the current configured network matches |networkId|. + * + * @param networkId network id of the network to be removed from supplicant. + */ + public void removeNetworkIfCurrent(int networkId) { + synchronized (mLock) { + if (getCurrentNetworkId() == networkId) { + // Currently we only save 1 network in supplicant. + removeAllNetworks(); + } + } + } + + /** * Remove all networks from supplicant */ public boolean removeAllNetworks() { @@ -516,8 +544,8 @@ public class SupplicantStaIfaceHal { } // Reset current network info. Probably not needed once we add support to remove/reset // current network on receiving disconnection event from supplicant (b/32898136). - mFrameworkNetworkId = WifiConfiguration.INVALID_NETWORK_ID; - mCurrentNetwork = null; + mCurrentNetworkLocalConfig = null; + mCurrentNetworkRemoteHandle = null; return true; } @@ -528,8 +556,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean setCurrentNetworkBssid(String bssidStr) { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.setBssid(bssidStr); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.setBssid(bssidStr); } /** @@ -538,8 +566,8 @@ public class SupplicantStaIfaceHal { * @return Hex string corresponding to the WPS NFC token. */ public String getCurrentNetworkWpsNfcConfigurationToken() { - if (mCurrentNetwork == null) return null; - return mCurrentNetwork.getWpsNfcConfigurationToken(); + if (mCurrentNetworkRemoteHandle == null) return null; + return mCurrentNetworkRemoteHandle.getWpsNfcConfigurationToken(); } /** @@ -548,8 +576,8 @@ public class SupplicantStaIfaceHal { * @return anonymous identity string if succeeds, null otherwise. */ public String getCurrentNetworkEapAnonymousIdentity() { - if (mCurrentNetwork == null) return null; - return mCurrentNetwork.fetchEapAnonymousIdentity(); + if (mCurrentNetworkRemoteHandle == null) return null; + return mCurrentNetworkRemoteHandle.fetchEapAnonymousIdentity(); } /** @@ -559,8 +587,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean sendCurrentNetworkEapIdentityResponse(String identityStr) { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.sendNetworkEapIdentityResponse(identityStr); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.sendNetworkEapIdentityResponse(identityStr); } /** @@ -570,8 +598,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean sendCurrentNetworkEapSimGsmAuthResponse(String paramsStr) { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.sendNetworkEapSimGsmAuthResponse(paramsStr); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthResponse(paramsStr); } /** @@ -580,8 +608,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean sendCurrentNetworkEapSimGsmAuthFailure() { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.sendNetworkEapSimGsmAuthFailure(); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthFailure(); } /** @@ -591,8 +619,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean sendCurrentNetworkEapSimUmtsAuthResponse(String paramsStr) { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.sendNetworkEapSimUmtsAuthResponse(paramsStr); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr); } /** @@ -602,8 +630,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean sendCurrentNetworkEapSimUmtsAutsResponse(String paramsStr) { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.sendNetworkEapSimUmtsAutsResponse(paramsStr); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr); } /** @@ -612,8 +640,8 @@ public class SupplicantStaIfaceHal { * @return true if succeeds, false otherwise. */ public boolean sendCurrentNetworkEapSimUmtsAuthFailure() { - if (mCurrentNetwork == null) return false; - return mCurrentNetwork.sendNetworkEapSimUmtsAuthFailure(); + if (mCurrentNetworkRemoteHandle == null) return false; + return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthFailure(); } /** @@ -1858,10 +1886,10 @@ public class SupplicantStaIfaceHal { mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE); if (newSupplicantState == SupplicantState.COMPLETED) { mWifiMonitor.broadcastNetworkConnectionEvent( - mIfaceName, mFrameworkNetworkId, bssidStr); + mIfaceName, getCurrentNetworkId(), bssidStr); } mWifiMonitor.broadcastSupplicantStateChangeEvent( - mIfaceName, mFrameworkNetworkId, wifiSsid, bssidStr, newSupplicantState); + mIfaceName, getCurrentNetworkId(), wifiSsid, bssidStr, newSupplicantState); } } diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index 850c5aebe..9c90bcf47 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -218,6 +218,7 @@ public class WifiApConfigStore { R.string.wifi_localhotspot_configure_ssid_default) + "_" + getRandomIntForDefaultSsid(); config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); + config.networkId = WifiConfiguration.LOCAL_ONLY_NETWORK_ID; String randomUUID = UUID.randomUUID().toString(); // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13); diff --git a/service/java/com/android/server/wifi/WifiBackupRestore.java b/service/java/com/android/server/wifi/WifiBackupRestore.java index 4095d8283..60c3b488d 100644 --- a/service/java/com/android/server/wifi/WifiBackupRestore.java +++ b/service/java/com/android/server/wifi/WifiBackupRestore.java @@ -28,6 +28,7 @@ import android.util.Xml; import com.android.internal.util.FastXmlSerializer; import com.android.server.net.IpConfigStore; import com.android.server.wifi.util.NativeUtil; +import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.XmlUtil; import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil; import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil; @@ -96,6 +97,7 @@ public class WifiBackupRestore { private static final String WEP_KEYS_MASK_SEARCH_PATTERN = "(<.*=)(.*)(/>)"; private static final String WEP_KEYS_MASK_REPLACE_PATTERN = "$1*$3"; + private final WifiPermissionsUtil mWifiPermissionsUtil; /** * Verbose logging flag. */ @@ -109,6 +111,10 @@ public class WifiBackupRestore { private byte[] mDebugLastBackupDataRestored; private byte[] mDebugLastSupplicantBackupDataRestored; + public WifiBackupRestore(WifiPermissionsUtil wifiPermissionsUtil) { + mWifiPermissionsUtil = wifiPermissionsUtil; + } + /** * Retrieve an XML byte stream representing the data that needs to be backed up from the * provided configurations. @@ -163,7 +169,9 @@ public class WifiBackupRestore { if (configuration.isEnterprise() || configuration.isPasspoint()) { continue; } - if (configuration.creatorUid >= Process.FIRST_APPLICATION_UID) { + if (!mWifiPermissionsUtil.checkConfigOverridePermission(configuration.creatorUid)) { + Log.d(TAG, "Ignoring network from an app with no config override permission: " + + configuration.configKey()); continue; } // Write this configuration data now. @@ -702,6 +710,8 @@ public class WifiBackupRestore { Integer.parseInt(extras.get( SupplicantStaNetworkHal.ID_STRING_KEY_CREATOR_UID)); if (creatorUid >= Process.FIRST_APPLICATION_UID) { + Log.d(TAG, "Ignoring network from non-system app: " + + configuration.configKey()); return null; } } diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 3f31c7ff8..4b2bb1c49 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -152,10 +152,29 @@ public class WifiConfigManager { */ public interface OnSavedNetworkUpdateListener { /** - * Invoked on saved network being enabled, disabled, blacklisted or - * un-blacklisted. + * Invoked on saved network being added. */ - void onSavedNetworkUpdate(); + void onSavedNetworkAdded(int networkId); + /** + * Invoked on saved network being enabled. + */ + void onSavedNetworkEnabled(int networkId); + /** + * Invoked on saved network being permanently disabled. + */ + void onSavedNetworkPermanentlyDisabled(int networkId); + /** + * Invoked on saved network being removed. + */ + void onSavedNetworkRemoved(int networkId); + /** + * Invoked on saved network being temporarily disabled. + */ + void onSavedNetworkTemporarilyDisabled(int networkId); + /** + * Invoked on saved network being updated. + */ + void onSavedNetworkUpdated(int networkId); } /** * Max size of scan details to cache in {@link #mScanDetailCaches}. @@ -1039,7 +1058,13 @@ public class WifiConfigManager { // Unless the added network is ephemeral or Passpoint, persist the network update/addition. if (!config.ephemeral && !config.isPasspoint()) { saveToStore(true); - if (mListener != null) mListener.onSavedNetworkUpdate(); + if (mListener != null) { + if (result.isNewNetwork()) { + mListener.onSavedNetworkAdded(newConfig.networkId); + } else { + mListener.onSavedNetworkUpdated(newConfig.networkId); + } + } } return result; } @@ -1104,7 +1129,7 @@ public class WifiConfigManager { // Unless the removed network is ephemeral or Passpoint, persist the network removal. if (!config.ephemeral && !config.isPasspoint()) { saveToStore(true); - if (mListener != null) mListener.onSavedNetworkUpdate(); + if (mListener != null) mListener.onSavedNetworkRemoved(networkId); } return true; } @@ -1165,7 +1190,8 @@ public class WifiConfigManager { /** * Helper method to mark a network enabled for network selection. */ - private void setNetworkSelectionEnabled(NetworkSelectionStatus status) { + private void setNetworkSelectionEnabled(WifiConfiguration config) { + NetworkSelectionStatus status = config.getNetworkSelectionStatus(); status.setNetworkSelectionStatus( NetworkSelectionStatus.NETWORK_SELECTION_ENABLED); status.setDisableTime( @@ -1174,32 +1200,35 @@ public class WifiConfigManager { // Clear out all the disable reason counters. status.clearDisableReasonCounter(); - if (mListener != null) mListener.onSavedNetworkUpdate(); + if (mListener != null) mListener.onSavedNetworkEnabled(config.networkId); } /** * Helper method to mark a network temporarily disabled for network selection. */ private void setNetworkSelectionTemporarilyDisabled( - NetworkSelectionStatus status, int disableReason) { + WifiConfiguration config, int disableReason) { + NetworkSelectionStatus status = config.getNetworkSelectionStatus(); status.setNetworkSelectionStatus( NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); // Only need a valid time filled in for temporarily disabled networks. status.setDisableTime(mClock.getElapsedSinceBootMillis()); status.setNetworkSelectionDisableReason(disableReason); + if (mListener != null) mListener.onSavedNetworkTemporarilyDisabled(config.networkId); } /** * Helper method to mark a network permanently disabled for network selection. */ private void setNetworkSelectionPermanentlyDisabled( - NetworkSelectionStatus status, int disableReason) { + WifiConfiguration config, int disableReason) { + NetworkSelectionStatus status = config.getNetworkSelectionStatus(); status.setNetworkSelectionStatus( NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); status.setDisableTime( NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); status.setNetworkSelectionDisableReason(disableReason); - if (mListener != null) mListener.onSavedNetworkUpdate(); + if (mListener != null) mListener.onSavedNetworkPermanentlyDisabled(config.networkId); } /** @@ -1230,12 +1259,12 @@ public class WifiConfigManager { return false; } if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { - setNetworkSelectionEnabled(networkStatus); + setNetworkSelectionEnabled(config); setNetworkStatus(config, WifiConfiguration.Status.ENABLED); } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) { - setNetworkSelectionTemporarilyDisabled(networkStatus, reason); + setNetworkSelectionTemporarilyDisabled(config, reason); } else { - setNetworkSelectionPermanentlyDisabled(networkStatus, reason); + setNetworkSelectionPermanentlyDisabled(config, reason); setNetworkStatus(config, WifiConfiguration.Status.DISABLED); } localLog("setNetworkSelectionStatus: configKey=" + config.configKey() diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java index 67f1faded..c726f4966 100644 --- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java +++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java @@ -235,6 +235,41 @@ public class WifiConfigurationUtil { } /** + * Check if the provided two networks are the same. + * + * @param config Configuration corresponding to a network. + * @param config1 Configuration corresponding to another network. + * + * @return true if |config| and |config1| are the same network. + * false otherwise. + */ + public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) { + if (config == null && config1 == null) { + return true; + } + if (config == null || config1 == null) { + return false; + } + if (config.networkId != config1.networkId) { + return false; + } + if (!Objects.equals(config.SSID, config1.SSID)) { + return false; + } + String networkSelectionBSSID = config.getNetworkSelectionStatus() + .getNetworkSelectionBSSID(); + String networkSelectionBSSID1 = config1.getNetworkSelectionStatus() + .getNetworkSelectionBSSID(); + if (!Objects.equals(networkSelectionBSSID, networkSelectionBSSID1)) { + return false; + } + if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) { + return false; + } + return true; + } + + /** * Create a PnoNetwork object from the provided WifiConfiguration. * * @param config Configuration corresponding to the network. @@ -261,6 +296,7 @@ public class WifiConfigurationUtil { return pnoNetwork; } + /** * General WifiConfiguration list sorting algorithm: * 1, Place the fully enabled networks first. diff --git a/service/java/com/android/server/wifi/WifiConnectivityHelper.java b/service/java/com/android/server/wifi/WifiConnectivityHelper.java index 6016b57b5..4aac31168 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityHelper.java +++ b/service/java/com/android/server/wifi/WifiConnectivityHelper.java @@ -161,4 +161,14 @@ public class WifiConnectivityHelper { return mWifiNative.configureRoaming(roamConfig); } + + /** + * Remove the request |networkId| from supplicant if it's the current network, + * if the current configured network matches |networkId|. + * + * @param networkId network id of the network to be removed from supplicant. + */ + public void removeNetworkIfCurrent(int networkId) { + mWifiNative.removeNetworkIfCurrent(networkId); + } } diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index 6d82ce869..f45d17b41 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -487,7 +487,32 @@ public class WifiConnectivityManager { private class OnSavedNetworkUpdateListener implements WifiConfigManager.OnSavedNetworkUpdateListener { - public void onSavedNetworkUpdate() { + @Override + public void onSavedNetworkAdded(int networkId) { + updatePnoScan(); + } + @Override + public void onSavedNetworkEnabled(int networkId) { + updatePnoScan(); + } + @Override + public void onSavedNetworkRemoved(int networkId) { + updatePnoScan(); + } + @Override + public void onSavedNetworkUpdated(int networkId) { + updatePnoScan(); + } + @Override + public void onSavedNetworkTemporarilyDisabled(int networkId) { + mConnectivityHelper.removeNetworkIfCurrent(networkId); + } + @Override + public void onSavedNetworkPermanentlyDisabled(int networkId) { + mConnectivityHelper.removeNetworkIfCurrent(networkId); + updatePnoScan(); + } + private void updatePnoScan() { // Update the PNO scan network list when screen is off. Here we // rely on startConnectivityScan() to perform all the checks and clean up. if (!mScreenOn) { diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index ed82b2b2d..c1b186142 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -30,7 +30,6 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; @@ -480,7 +479,7 @@ public class WifiController extends StateMachine { if (msg.arg2 == 0) { // previous wifi state has not been saved yet mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); } - mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj, + mWifiStateMachine.setHostApRunning((SoftApModeConfiguration) msg.obj, true); transitionTo(mApEnabledState); } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 90e400cc1..c51778578 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -96,7 +96,7 @@ public class WifiInjector { private final PropertyService mPropertyService = new SystemPropertyService(); private final BuildProperties mBuildProperties = new SystemBuildProperties(); private final KeyStore mKeyStore = KeyStore.getInstance(); - private final WifiBackupRestore mWifiBackupRestore = new WifiBackupRestore(); + private final WifiBackupRestore mWifiBackupRestore; private final WifiMulticastLockManager mWifiMulticastLockManager; private final WifiConfigStore mWifiConfigStore; private final WifiKeyStore mWifiKeyStore; @@ -150,6 +150,7 @@ public class WifiInjector { mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE); mWifiPermissionsUtil = new WifiPermissionsUtil(mWifiPermissionsWrapper, mContext, mSettingsStore, UserManager.get(mContext), mNetworkScoreManager, this); + mWifiBackupRestore = new WifiBackupRestore(mWifiPermissionsUtil); mBatteryStats = IBatteryStats.Stub.asInterface(mFrameworkFacade.getService( BatteryStats.SERVICE_NAME)); mWifiStateTracker = new WifiStateTracker(mBatteryStats); diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index 385bfccaa..b2fc56e25 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -179,9 +179,9 @@ public class WifiMonitor { if (mConnected) { return true; } - if (connectTries++ < 5) { + if (connectTries++ < 50) { try { - Thread.sleep(1000); + Thread.sleep(100); } catch (InterruptedException ignore) { } } else { diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index abe692346..90f6ac195 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -787,6 +787,16 @@ public class WifiNative { public String getCurrentNetworkWpsNfcConfigurationToken() { return mSupplicantStaIfaceHal.getCurrentNetworkWpsNfcConfigurationToken(); } + + /** Remove the request |networkId| from supplicant if it's the current network, + * if the current configured network matches |networkId|. + * + * @param networkId network id of the network to be removed from supplicant. + */ + public void removeNetworkIfCurrent(int networkId) { + mSupplicantStaIfaceHal.removeNetworkIfCurrent(networkId); + } + /******************************************************** * Vendor HAL operations ********************************************************/ diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index ec05f7cec..5c9548a54 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL; @@ -26,8 +28,6 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; -import static com.android.server.connectivity.tethering.IControlsTethering.STATE_LOCAL_ONLY; -import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED; import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED; import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED; @@ -485,13 +485,17 @@ public class WifiServiceImpl extends IWifiManager.Stub { new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - final int currentState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, + final int currState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); final int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); final int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON, HOTSPOT_NO_ERROR); - handleWifiApStateChange(currentState, prevState, errorCode); + final String ifaceName = + intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); + final int mode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, + WifiManager.IFACE_IP_MODE_UNSPECIFIED); + handleWifiApStateChange(currState, prevState, errorCode, ifaceName, mode); } }, new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)); @@ -818,7 +822,9 @@ public class WifiServiceImpl extends IWifiManager.Stub { } // null wifiConfig is a meaningful input for CMD_SET_AP if (wifiConfig == null || isValid(wifiConfig)) { - mWifiController.sendMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig); + int mode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; + SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig); + mWifiController.sendMessage(CMD_SET_AP, enabled ? 1 : 0, 0, softApConfig); } else { Slog.e(TAG, "Invalid WifiConfiguration"); } @@ -892,14 +898,12 @@ public class WifiServiceImpl extends IWifiManager.Stub { // between a tether request and a hotspot request (tethering wins). sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE); - mLocalOnlyHotspotRequests.clear(); break; case WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR: // there was an error setting up the hotspot... trigger onFailed for the // registered LOHS requestors sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( LocalOnlyHotspotCallback.ERROR_GENERIC); - mLocalOnlyHotspotRequests.clear(); updateInterfaceIpStateInternal(null, WifiManager.IFACE_IP_MODE_UNSPECIFIED); break; case WifiManager.IFACE_IP_MODE_UNSPECIFIED: @@ -930,12 +934,15 @@ public class WifiServiceImpl extends IWifiManager.Stub { mLog.trace("startSoftAp uid=%").c(Binder.getCallingUid()).flush(); - // TODO: determine if we need to stop softap and clean up state if a tethering request comes - // from the user while we are just setting up. For now, the second CMD_START_AP will be - // ignored in WifiStateMachine. This will still bring up tethering and the registered LOHS - // requests will be cleared when we get the interface ip tethered status. + synchronized (mLocalOnlyHotspotRequests) { + // If a tethering request comes in while we have LOHS running (or requested), call stop + // for softap mode and restart softap with the tethering config. + if (!mLocalOnlyHotspotRequests.isEmpty()) { + stopSoftApInternal(); + } - return startSoftApInternal(wifiConfig, STATE_TETHERED); + return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED); + } } /** @@ -948,8 +955,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { // null wifiConfig is a meaningful input for CMD_SET_AP if (wifiConfig == null || isValid(wifiConfig)) { - // TODO: need a way to set the mode - mWifiController.sendMessage(CMD_SET_AP, 1, 0, wifiConfig); + SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig); + mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig); return true; } Slog.e(TAG, "Invalid WifiConfiguration"); @@ -966,12 +973,21 @@ public class WifiServiceImpl extends IWifiManager.Stub { // NETWORK_STACK is a signature only permission. enforceNetworkStackPermission(); + // only permitted callers are allowed to this point - they must have gone through + // connectivity service since this method is protected with the NETWORK_STACK PERMISSION + mLog.trace("stopSoftAp uid=%").c(Binder.getCallingUid()).flush(); - // add checks here to make sure this is the proper caller - apps can't disable tethering or - // instances of local only hotspot that they didn't start. return false for those cases + synchronized (mLocalOnlyHotspotRequests) { + // If a tethering request comes in while we have LOHS running (or requested), call stop + // for softap mode and restart softap with the tethering config. + if (!mLocalOnlyHotspotRequests.isEmpty()) { + mLog.trace("Call to stop Tethering while LOHS is active," + + " Registered LOHS callers will be updated when softap stopped."); + } - return stopSoftApInternal(); + return stopSoftApInternal(); + } } /** @@ -981,11 +997,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { private boolean stopSoftApInternal() { mLog.trace("stopSoftApInternal uid=%").c(Binder.getCallingUid()).flush(); - // we have an allowed caller - clear local only hotspot if it was enabled - synchronized (mLocalOnlyHotspotRequests) { - mLocalOnlyHotspotRequests.clear(); - mLocalOnlyHotspotConfig = null; - } mWifiController.sendMessage(CMD_SET_AP, 0, 0); return true; } @@ -993,10 +1004,12 @@ public class WifiServiceImpl extends IWifiManager.Stub { /** * Private method to handle SoftAp state changes */ - private void handleWifiApStateChange(int currentState, int previousState, int errorCode) { + private void handleWifiApStateChange( + int currentState, int previousState, int errorCode, String ifaceName, int mode) { // The AP state update from WifiStateMachine for softap Slog.d(TAG, "handleWifiApStateChange: currentState=" + currentState - + " previousState=" + previousState + " errorCode= " + errorCode); + + " previousState=" + previousState + " errorCode= " + errorCode + + " ifaceName=" + ifaceName + " mode=" + mode); // check if we have a failure - since it is possible (worst case scenario where // WifiController and WifiStateMachine are out of sync wrt modes) to get two FAILED @@ -1050,6 +1063,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { try { requestor.sendHotspotFailedMessage(arg1); + requestor.unlinkDeathRecipient(); } catch (RemoteException e) { // This will be cleaned up by binder death handling } @@ -1069,6 +1083,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { try { requestor.sendHotspotStoppedMessage(); + requestor.unlinkDeathRecipient(); } catch (RemoteException e) { // This will be cleaned up by binder death handling } @@ -1144,6 +1159,16 @@ public class WifiServiceImpl extends IWifiManager.Stub { return LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED; } + // the app should be in the foreground + try { + if (!mFrameworkFacade.isAppForeground(uid)) { + return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; + } + } catch (RemoteException e) { + mLog.trace("RemoteException during isAppForeground when calling startLOHS"); + return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; + } + mLog.trace("startLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush(); synchronized (mLocalOnlyHotspotRequests) { @@ -1179,7 +1204,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { // this is the first request, then set up our config and start LOHS mLocalOnlyHotspotConfig = WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext); - startSoftApInternal(mLocalOnlyHotspotConfig, STATE_LOCAL_ONLY); + startSoftApInternal(mLocalOnlyHotspotConfig, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } mLocalOnlyHotspotRequests.put(pid, request); @@ -2383,8 +2408,9 @@ public class WifiServiceImpl extends IWifiManager.Stub { } if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) { - // Turn mobile hotspot off - setWifiApEnabled(null, false); + // Turn mobile hotspot off - will also clear any registered LOHS requests when it is + // shut down + stopSoftApInternal(); } if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) { @@ -2495,8 +2521,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public byte[] retrieveBackupData() { - enforceReadCredentialPermission(); - enforceAccessPermission(); + enforceNetworkSettingsPermission(); mLog.trace("retrieveBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel == null) { Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); @@ -2541,7 +2566,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public void restoreBackupData(byte[] data) { - enforceChangePermission(); + enforceNetworkSettingsPermission(); mLog.trace("restoreBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel == null) { Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); @@ -2563,7 +2588,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { * @param ipConfigData Raw byte stream of ipconfig.txt */ public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) { - enforceChangePermission(); + enforceNetworkSettingsPermission(); mLog.trace("restoreSupplicantBackupData uid=%").c(Binder.getCallingUid()).flush(); if (mWifiStateMachineChannel == null) { Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index 7f57316a6..19c4812b0 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -1607,7 +1607,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * TODO: doc */ - public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) { + public void setHostApRunning(SoftApModeConfiguration wifiConfig, boolean enable) { if (enable) { sendMessage(CMD_START_AP, wifiConfig); } else { @@ -2806,7 +2806,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private void setWifiApState(int wifiApState, int reason) { + private void setWifiApState(int wifiApState, int reason, String ifaceName, int mode) { final int previousWifiApState = mWifiApState.get(); try { @@ -2833,6 +2833,12 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); } + if (ifaceName == null) { + loge("Updating wifiApState with a null iface name"); + } + intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, ifaceName); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mode); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -4277,7 +4283,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_START_AP: /* Cannot start soft AP while in client mode */ loge("Failed to start soft AP with a running supplicant"); - setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); + setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL, + null, WifiManager.IFACE_IP_MODE_UNSPECIFIED); break; case CMD_SET_OPERATIONAL_MODE: mOperationalMode = message.arg1; @@ -6667,6 +6674,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss class SoftApState extends State { private SoftApManager mSoftApManager; + private String mIfaceName; + private int mMode; private class SoftApListener implements SoftApManager.Listener { @Override @@ -6677,7 +6686,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss sendMessage(CMD_START_AP_FAILURE); } - setWifiApState(state, reason); + setWifiApState(state, reason, mIfaceName, mMode); } } @@ -6687,11 +6696,13 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (message.what != CMD_START_AP) { throw new RuntimeException("Illegal transition to SoftApState: " + message); } + SoftApModeConfiguration config = (SoftApModeConfiguration) message.obj; + mMode = config.getTargetMode(); IApInterface apInterface = mWifiNative.setupForSoftApMode(); if (apInterface == null) { setWifiApState(WIFI_AP_STATE_FAILED, - WifiManager.SAP_START_FAILURE_GENERAL); + WifiManager.SAP_START_FAILURE_GENERAL, null, mMode); /** * Transition to InitialState to reset the * driver/HAL back to the initial state. @@ -6700,13 +6711,19 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss return; } - WifiConfiguration config = (WifiConfiguration) message.obj; + try { + mIfaceName = apInterface.getInterfaceName(); + } catch (RemoteException e) { + // Failed to get the interface name. The name will not be available for + // the enabled broadcast, but since we had an error getting the name, we most likely + // won't be able to fully start softap mode. + } checkAndSetConnectivityInstance(); mSoftApManager = mWifiInjector.makeSoftApManager(mNwService, new SoftApListener(), apInterface, - config); + config.getWifiConfiguration()); mSoftApManager.start(); mWifiStateTracker.updateState(WifiStateTracker.SOFT_AP); } @@ -6714,6 +6731,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss @Override public void exit() { mSoftApManager = null; + mIfaceName = null; + mMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; } @Override diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java index 9c1ae94b5..88f189814 100644 --- a/service/java/com/android/server/wifi/WifiVendorHal.java +++ b/service/java/com/android/server/wifi/WifiVendorHal.java @@ -64,6 +64,7 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.net.wifi.WifiWakeReasonAndCounts; +import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.MutableBoolean; @@ -204,16 +205,23 @@ public class WifiVendorHal { private IWifiRttController mIWifiRttController; private final HalDeviceManager mHalDeviceManager; private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks; - private final Looper mLooper; private final IWifiStaIfaceEventCallback mIWifiStaIfaceEventCallback; private final IWifiChipEventCallback mIWifiChipEventCallback; private final RttEventCallback mRttEventCallback; + // Plumbing for event handling. + // + // Being final fields, they can be accessed without synchronization under + // some reasonable assumptions. See + // https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5 + private final Looper mLooper; + private final Handler mHalEventHandler; public WifiVendorHal(HalDeviceManager halDeviceManager, Looper looper) { mHalDeviceManager = halDeviceManager; mLooper = looper; + mHalEventHandler = new Handler(looper); mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener(); mIWifiStaIfaceEventCallback = new StaIfaceEventCallback(); mIWifiChipEventCallback = new ChipEventCallback(); @@ -2455,25 +2463,47 @@ public class WifiVendorHal { WifiDebugRingBufferStatus status, java.util.ArrayList<Byte> data) { //TODO(b/35875078) Reinstate logging when execessive callbacks are fixed // mVerboseLog.d("onDebugRingBufferDataAvailable " + status); - WifiNative.WifiLoggerEventHandler eventHandler; - synchronized (sLock) { - if (mLogEventHandler == null || status == null || data == null) return; - eventHandler = mLogEventHandler; - } - eventHandler.onRingBufferData( - ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data)); + mHalEventHandler.post(() -> { + WifiNative.WifiLoggerEventHandler eventHandler; + synchronized (sLock) { + if (mLogEventHandler == null || status == null || data == null) return; + eventHandler = mLogEventHandler; + } + // Because |sLock| has been released, there is a chance that we'll execute + // a spurious callback (after someone has called resetLogHandler()). + // + // However, the alternative risks deadlock. Consider: + // [T1.1] WifiDiagnostics.captureBugReport() + // [T1.2] -- acquire WifiDiagnostics object's intrinsic lock + // [T1.3] -> WifiVendorHal.getRingBufferData() + // [T1.4] -- acquire WifiVendorHal.sLock + // [T2.1] <lambda>() + // [T2.2] -- acquire WifiVendorHal.sLock + // [T2.3] -> WifiDiagnostics.onRingBufferData() + // [T2.4] -- acquire WifiDiagnostics object's intrinsic lock + // + // The problem here is that the two threads acquire the locks in opposite order. + // If, for example, T2.2 executes between T1.2 and 1.4, then T1 and T2 + // will be deadlocked. + eventHandler.onRingBufferData( + ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data)); + }); } @Override public void onDebugErrorAlert(int errorCode, java.util.ArrayList<Byte> debugData) { mVerboseLog.d("onDebugErrorAlert " + errorCode); - WifiNative.WifiLoggerEventHandler eventHandler; - synchronized (sLock) { - if (mLogEventHandler == null || debugData == null) return; - eventHandler = mLogEventHandler; - } - eventHandler.onWifiAlert( - errorCode, NativeUtil.byteArrayFromArrayList(debugData)); + mHalEventHandler.post(() -> { + WifiNative.WifiLoggerEventHandler eventHandler; + synchronized (sLock) { + if (mLogEventHandler == null || debugData == null) return; + eventHandler = mLogEventHandler; + } + // See comment in onDebugRingBufferDataAvailable(), for an explanation + // of why this callback is invoked without |sLock| held. + eventHandler.onWifiAlert( + errorCode, NativeUtil.byteArrayFromArrayList(debugData)); + }); } } diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java b/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java index 0411f3073..e14c10c91 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java @@ -158,9 +158,9 @@ public class WifiP2pMonitor { if (mConnected) { return true; } - if (connectTries++ < 5) { + if (connectTries++ < 50) { try { - Thread.sleep(1000); + Thread.sleep(100); } catch (InterruptedException ignore) { } } else { diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index 771e336d5..08c9e1359 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -447,7 +447,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private RequestList<ScanSettings> mActiveScans = new RequestList<>(); private RequestList<ScanSettings> mPendingScans = new RequestList<>(); - // Scan results cached from the last full single scan request. private ScanResult[] mCachedScanResults = new ScanResult[0]; WifiSingleScanStateMachine(Looper looper) { @@ -880,11 +879,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults); } - // Since we use NoBandChannelHelper, as long as a specific band is mentioned, the scan - // request is treated as full band (WifiScanner.WIFI_BAND_*). - if (results.isAllChannelsScanned()) { - mCachedScanResults = results.getResults(); - } + // Cache the results here so that apps can retrieve them. + mCachedScanResults = results.getResults(); sendScanResultBroadcast(true); } diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java index b088cc01d..4aa9bc972 100644 --- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java @@ -563,6 +563,16 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call } mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray); } + // On pno scan result event, we are expecting a mLastScanSettings for pno scan. + // However, if unlikey mLastScanSettings is for single scan, we need this part + // to protect from leaving WifiSingleScanStateMachine in a forever wait state. + if (mLastScanSettings.singleScanActive + && mLastScanSettings.singleScanEventHandler != null) { + Log.w(TAG, "Polling pno scan result when single scan is active, reporting" + + " single scan failure"); + mLastScanSettings.singleScanEventHandler + .onScanStatus(WifiNative.WIFI_SCAN_FAILED); + } // mLastScanSettings is for either single/batched scan or pno scan. // We can safely set it to null when pno scan finishes. mLastScanSettings = null; diff --git a/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java index 78a3a3d0f..7f6360408 100644 --- a/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java @@ -172,6 +172,33 @@ public class SavedNetworkEvaluatorTest { } /** + * Do not evaluate networks that {@link WifiConfiguration#isEphemeral}. + */ + @Test + public void ignoreEphemeralNetworks() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[ESS]", "[ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10}; + int[] securities = {SECURITY_NONE, SECURITY_NONE}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + for (WifiConfiguration wifiConfiguration : savedConfigs) { + wifiConfiguration.ephemeral = true; + } + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, null); + + assertNull(candidate); + } + + /** * Set the candidate {@link ScanResult} for all {@link WifiConfiguration}s regardless of * whether they are secure saved, open saved, or {@link WifiConfiguration#useExternalScores}. */ diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java index ef393f364..2deef52e4 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java @@ -457,13 +457,35 @@ public class SupplicantStaIfaceHalTest { executeAndValidateConnectSequence(0, false); } - /** - * Tests connection to a specified network with single existing network. - */ @Test - public void testConnectWithSingleExistingNetwork() throws Exception { + public void testConnectToNetworkWithDifferentConfigReplacesNetworkInSupplicant() + throws Exception { executeAndValidateInitializationSequence(); - executeAndValidateConnectSequence(0, true); + WifiConfiguration config = executeAndValidateConnectSequence( + SUPPLICANT_NETWORK_ID, false); + // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection. + reset(mISupplicantStaIfaceMock); + setupMocksForConnectSequence(true /*haveExistingNetwork*/); + // Make this network different by changing SSID. + config.SSID = "AnDifferentSSID"; + assertTrue(mDut.connectToNetwork(config)); + verify(mISupplicantStaIfaceMock).removeNetwork(SUPPLICANT_NETWORK_ID); + verify(mISupplicantStaIfaceMock) + .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class)); + } + + @Test + public void connectToNetworkWithSameNetworkDoesNotRemoveNetworkFromSupplicant() + throws Exception { + executeAndValidateInitializationSequence(); + WifiConfiguration config = executeAndValidateConnectSequence(SUPPLICANT_NETWORK_ID, false); + // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection. + reset(mISupplicantStaIfaceMock); + setupMocksForConnectSequence(true /*haveExistingNetwork*/); + assertTrue(mDut.connectToNetwork(config)); + verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt()); + verify(mISupplicantStaIfaceMock, never()) + .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class)); } /** @@ -481,7 +503,7 @@ public class SupplicantStaIfaceHalTest { }).when(mISupplicantStaIfaceMock).addNetwork( any(ISupplicantStaIface.addNetworkCallback.class)); - assertFalse(mDut.connectToNetwork(new WifiConfiguration())); + assertFalse(mDut.connectToNetwork(createTestWifiConfiguration())); } /** @@ -495,7 +517,7 @@ public class SupplicantStaIfaceHalTest { when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class))) .thenReturn(false); - assertFalse(mDut.connectToNetwork(new WifiConfiguration())); + assertFalse(mDut.connectToNetwork(createTestWifiConfiguration())); // We should have removed the existing network once before connection and once more // on failure to save network configuration. verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt()); @@ -513,7 +535,7 @@ public class SupplicantStaIfaceHalTest { .when(mSupplicantStaNetworkMock).saveWifiConfiguration( any(WifiConfiguration.class)); - assertFalse(mDut.connectToNetwork(new WifiConfiguration())); + assertFalse(mDut.connectToNetwork(createTestWifiConfiguration())); // We should have removed the existing network once before connection and once more // on failure to save network configuration. verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt()); @@ -529,7 +551,7 @@ public class SupplicantStaIfaceHalTest { when(mSupplicantStaNetworkMock.select()).thenReturn(false); - assertFalse(mDut.connectToNetwork(new WifiConfiguration())); + assertFalse(mDut.connectToNetwork(createTestWifiConfiguration())); } /** @@ -539,6 +561,7 @@ public class SupplicantStaIfaceHalTest { public void testRoamToSameNetwork() throws Exception { executeAndValidateInitializationSequence(); executeAndValidateRoamSequence(true); + assertTrue(mDut.connectToNetwork(createTestWifiConfiguration())); } /** @@ -1282,6 +1305,12 @@ public class SupplicantStaIfaceHalTest { verify(mISupplicantStaIfaceMock).startWpsPbc(eq(anyBssidBytes)); } + private WifiConfiguration createTestWifiConfiguration() { + WifiConfiguration config = new WifiConfiguration(); + config.networkId = SUPPLICANT_NETWORK_ID; + return config; + } + private void executeAndValidateHs20DeauthImminentCallback(boolean isEss) throws Exception { executeAndValidateInitializationSequence(); assertNotNull(mISupplicantStaIfaceCallback); @@ -1493,14 +1522,16 @@ public class SupplicantStaIfaceHalTest { * * @param newFrameworkNetworkId Framework Network Id of the new network to connect. * @param haveExistingNetwork Removes the existing network. + * @return the WifiConfiguration object of the new network to connect. */ - private void executeAndValidateConnectSequence( + private WifiConfiguration executeAndValidateConnectSequence( final int newFrameworkNetworkId, final boolean haveExistingNetwork) throws Exception { setupMocksForConnectSequence(haveExistingNetwork); WifiConfiguration config = new WifiConfiguration(); config.networkId = newFrameworkNetworkId; assertTrue(mDut.connectToNetwork(config)); validateConnectSequence(haveExistingNetwork, 1); + return config; } /** diff --git a/tests/wifitests/src/com/android/server/wifi/TestUtil.java b/tests/wifitests/src/com/android/server/wifi/TestUtil.java index 90df07a61..a0a103026 100644 --- a/tests/wifitests/src/com/android/server/wifi/TestUtil.java +++ b/tests/wifitests/src/com/android/server/wifi/TestUtil.java @@ -74,14 +74,18 @@ public class TestUtil { * Send {@link WifiManager#WIFI_AP_STATE_CHANGED} broadcast. */ public static void sendWifiApStateChanged(BroadcastReceiver broadcastReceiver, - Context context, int apState, int previousState, int error) { + Context context, int apState, int previousState, int error, String ifaceName, + int mode) { Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, apState); intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousState); if (apState == WifiManager.WIFI_AP_STATE_FAILED) { - //only set reason number when softAP start failed + // only set reason number when softAP start failed intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, error); } + intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, ifaceName); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mode); + broadcastReceiver.onReceive(context, intent); } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index d4a3ff549..2d3b06695 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -202,5 +202,7 @@ public class WifiApConfigStoreTest { public void generateLocalOnlyHotspotConfigIsValid() { WifiConfiguration config = WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext); verifyDefaultApConfig(config, TEST_DEFAULT_HOTSPOT_SSID); + // The LOHS config should also have a specific network id set - check that as well. + assertEquals(WifiConfiguration.LOCAL_ONLY_NETWORK_ID, config.networkId); } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java index 362540517..58b8d394b 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java @@ -24,10 +24,13 @@ import android.os.Process; import android.test.suitebuilder.annotation.SmallTest; import com.android.server.net.IpConfigStore; +import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -48,11 +51,15 @@ import java.util.Random; @SmallTest public class WifiBackupRestoreTest { - private final WifiBackupRestore mWifiBackupRestore = new WifiBackupRestore(); + @Mock WifiPermissionsUtil mWifiPermissionsUtil; + private WifiBackupRestore mWifiBackupRestore; private boolean mCheckDump = true; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); + mWifiBackupRestore = new WifiBackupRestore(mWifiPermissionsUtil); // Enable verbose logging before tests to check the backup data dumps. mWifiBackupRestore.enableVerboseLogging(1); } @@ -361,25 +368,34 @@ public class WifiBackupRestoreTest { */ @Test public void testMultipleNetworksSystemAppBackupRestore() { + int systemAppUid = Process.SYSTEM_UID; + int nonSystemAppUid = Process.FIRST_APPLICATION_UID + 556; + when(mWifiPermissionsUtil.checkConfigOverridePermission(eq(systemAppUid))) + .thenReturn(true); + when(mWifiPermissionsUtil.checkConfigOverridePermission(eq(nonSystemAppUid))) + .thenReturn(false); + List<WifiConfiguration> configurations = new ArrayList<>(); List<WifiConfiguration> expectedConfigurations = new ArrayList<>(); WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork(); + wepNetwork.creatorUid = systemAppUid; configurations.add(wepNetwork); expectedConfigurations.add(wepNetwork); // These should not be in |expectedConfigurations|. WifiConfiguration nonSystemAppWepNetwork = WifiConfigurationTestUtil.createWepNetwork(); - nonSystemAppWepNetwork.creatorUid = Process.FIRST_APPLICATION_UID; + nonSystemAppWepNetwork.creatorUid = nonSystemAppUid; configurations.add(nonSystemAppWepNetwork); WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork(); + pskNetwork.creatorUid = systemAppUid; configurations.add(pskNetwork); expectedConfigurations.add(pskNetwork); // These should not be in |expectedConfigurations|. WifiConfiguration nonSystemAppPskNetwork = WifiConfigurationTestUtil.createPskNetwork(); - nonSystemAppPskNetwork.creatorUid = Process.FIRST_APPLICATION_UID + 1; + nonSystemAppPskNetwork.creatorUid = nonSystemAppUid; configurations.add(nonSystemAppPskNetwork); WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index f61013841..e85686fa7 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -247,6 +247,8 @@ public class WifiConfigManagerTest { networks.add(openNetwork); verifyAddNetworkToWifiConfigManager(openNetwork); + verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + reset(mWcmListener); // Now change BSSID for the network. assertAndSetNetworkBSSID(openNetwork, TEST_BSSID); @@ -257,7 +259,7 @@ public class WifiConfigManagerTest { mWifiConfigManager.getConfiguredNetworksWithPasswords(); WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate( networks, retrievedNetworks); - verify(mWcmListener, times(2)).onSavedNetworkUpdate(); + verify(mWcmListener).onSavedNetworkUpdated(openNetwork.networkId); } /** @@ -281,7 +283,7 @@ public class WifiConfigManagerTest { // Ensure that this is not returned in the saved network list. assertTrue(mWifiConfigManager.getSavedNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkUpdate(); + verify(mWcmListener, never()).onSavedNetworkAdded(ephemeralNetwork.networkId); } /** @@ -453,13 +455,16 @@ public class WifiConfigManagerTest { WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(); verifyAddNetworkToWifiConfigManager(openNetwork); + verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId); + reset(mWcmListener); + // Ensure that configured network list is not empty. assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty()); verifyRemoveNetworkFromWifiConfigManager(openNetwork); // Ensure that configured network list is empty now. assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, times(2)).onSavedNetworkUpdate(); + verify(mWcmListener).onSavedNetworkRemoved(openNetwork.networkId); } /** @@ -474,11 +479,12 @@ public class WifiConfigManagerTest { verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork); // Ensure that configured network list is not empty. assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty()); + verify(mWcmListener, never()).onSavedNetworkAdded(ephemeralNetwork.networkId); verifyRemoveEphemeralNetworkFromWifiConfigManager(ephemeralNetwork); // Ensure that configured network list is empty now. assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkUpdate(); + verify(mWcmListener, never()).onSavedNetworkRemoved(ephemeralNetwork.networkId); } /** @@ -492,11 +498,12 @@ public class WifiConfigManagerTest { verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork); // Ensure that configured network list is not empty. assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty()); + verify(mWcmListener, never()).onSavedNetworkAdded(passpointNetwork.networkId); verifyRemovePasspointNetworkFromWifiConfigManager(passpointNetwork); // Ensure that configured network list is empty now. assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty()); - verify(mWcmListener, never()).onSavedNetworkUpdate(); + verify(mWcmListener, never()).onSavedNetworkRemoved(passpointNetwork.networkId); } /** @@ -588,9 +595,10 @@ public class WifiConfigManagerTest { NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork); + int networkId = result.getNetworkId(); // First set it to enabled. verifyUpdateNetworkSelectionStatus( - result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); + networkId, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); // Now set it to temporarily disabled. The threshold for association rejection is 5, so // disable it 5 times to actually mark it temporarily disabled. @@ -600,16 +608,17 @@ public class WifiConfigManagerTest { for (int i = 1; i <= assocRejectThreshold; i++) { verifyUpdateNetworkSelectionStatus(result.getNetworkId(), assocRejectReason, i); } + verify(mWcmListener).onSavedNetworkTemporarilyDisabled(networkId); // Now set it to permanently disabled. verifyUpdateNetworkSelectionStatus( result.getNetworkId(), NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER, 0); - verify(mWcmListener, times(3)).onSavedNetworkUpdate(); + verify(mWcmListener).onSavedNetworkPermanentlyDisabled(networkId); // Now set it back to enabled. verifyUpdateNetworkSelectionStatus( result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0); - verify(mWcmListener, times(4)).onSavedNetworkUpdate(); + verify(mWcmListener, times(2)).onSavedNetworkEnabled(networkId); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java index 917a64cc9..b53732a91 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java @@ -241,6 +241,15 @@ public class WifiConfigurationTestUtil { return configuration; } + public static WifiConfiguration createPskNetwork(String ssid) { + WifiConfiguration configuration = + generateWifiConfig(TEST_NETWORK_ID, TEST_UID, ssid, true, true, null, + null, SECURITY_PSK); + configuration.preSharedKey = TEST_PSK; + return configuration; + } + + public static WifiConfiguration createPskHiddenNetwork() { WifiConfiguration configuration = createPskNetwork(); configuration.hiddenSSID = true; @@ -280,6 +289,14 @@ public class WifiConfigurationTestUtil { return configuration; } + public static WifiConfiguration createEapNetwork(String ssid) { + WifiConfiguration configuration = + generateWifiConfig(TEST_NETWORK_ID, TEST_UID, ssid, true, true, + null, null, SECURITY_EAP); + return configuration; + } + + public static WifiConfiguration createEapNetwork(int eapMethod, int phase2Method) { WifiConfiguration configuration = generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java index 1b4a16d1c..506db9150 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java @@ -41,6 +41,8 @@ public class WifiConfigurationUtilTest { static final int CURRENT_USER_ID = 0; static final int CURRENT_USER_MANAGED_PROFILE_USER_ID = 10; static final int OTHER_USER_ID = 11; + static final String TEST_SSID = "test_ssid"; + static final String TEST_SSID_1 = "test_ssid_1"; static final List<UserInfo> PROFILES = Arrays.asList( new UserInfo(CURRENT_USER_ID, "owner", 0), new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0)); @@ -239,6 +241,40 @@ public class WifiConfigurationUtilTest { } /** + * Verify that WifiConfigurationUtil.isSameNetwork returns true when two WifiConfiguration + * objects have the same parameters. + */ + @Test + public void testIsSameNetworkReturnsTrueOnSameNetwork() { + WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID); + WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID); + assertTrue(WifiConfigurationUtil.isSameNetwork(network, network1)); + } + + /** + * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration + * objects have the different SSIDs. + */ + @Test + public void testIsSameNetworkReturnsFalseOnDifferentSSID() { + WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID); + WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID_1); + assertFalse(WifiConfigurationUtil.isSameNetwork(network, network1)); + } + + /** + * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration + * objects have the different security type. + */ + @Test + public void testIsSameNetworkReturnsFalseOnDifferentSecurityType() { + WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID); + WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(TEST_SSID); + assertFalse(WifiConfigurationUtil.isSameNetwork(network, network1)); + } + + + /** * Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created * for a EAP network using {@link WifiConfigurationUtil#createPnoNetwork(WifiConfiguration, int) * }. diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index 7342cdb16..d5bfb2045 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -74,6 +74,7 @@ import android.os.Message; import android.os.Messenger; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; import android.os.UserManager; import android.os.WorkSource; import android.os.test.TestLooper; @@ -97,6 +98,7 @@ import org.mockito.Spy; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.List; /** * Unit tests for {@link WifiServiceImpl}. @@ -132,6 +134,8 @@ public class WifiServiceImplTest { ArgumentCaptor.forClass(IntentFilter.class); final ArgumentCaptor<Message> mMessageCaptor = ArgumentCaptor.forClass(Message.class); + final ArgumentCaptor<SoftApModeConfiguration> mSoftApModeConfigCaptor = + ArgumentCaptor.forClass(SoftApModeConfiguration.class); @Mock Context mContext; @Mock WifiInjector mWifiInjector; @@ -534,7 +538,9 @@ public class WifiServiceImplTest { when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) .thenReturn(false); mWifiServiceImpl.setWifiApEnabled(null, true); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(null)); + verify(mWifiController) + .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); + assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } /** @@ -549,7 +555,9 @@ public class WifiServiceImplTest { .thenReturn(false); WifiConfiguration apConfig = new WifiConfiguration(); mWifiServiceImpl.setWifiApEnabled(apConfig, true); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(apConfig)); + verify(mWifiController).sendMessage( + eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); + assertEquals(apConfig, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } /** @@ -562,7 +570,9 @@ public class WifiServiceImplTest { when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) .thenReturn(false); mWifiServiceImpl.setWifiApEnabled(null, false); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0), eq(null)); + verify(mWifiController) + .sendMessage(eq(CMD_SET_AP), eq(0), eq(0), mSoftApModeConfigCaptor.capture()); + assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } /** @@ -576,7 +586,8 @@ public class WifiServiceImplTest { // mApConfig is a mock and the values are not set - triggering the invalid config. Testing // will be improved when we actually do test softap configs in b/37280779 mWifiServiceImpl.setWifiApEnabled(mApConfig, true); - verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(mApConfig)); + verify(mWifiController, never()) + .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(SoftApModeConfiguration.class)); } /** @@ -613,7 +624,9 @@ public class WifiServiceImplTest { public void testStartSoftApWithPermissionsAndNullConfig() { boolean result = mWifiServiceImpl.startSoftAp(null); assertTrue(result); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(null)); + verify(mWifiController) + .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); + assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } /** @@ -634,7 +647,9 @@ public class WifiServiceImplTest { WifiConfiguration config = new WifiConfiguration(); boolean result = mWifiServiceImpl.startSoftAp(config); assertTrue(result); - verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(config)); + verify(mWifiController) + .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); + assertEquals(config, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); } /** @@ -747,6 +762,9 @@ public class WifiServiceImplTest { // allow test to proceed without a permission check failure when(mSettingsStore.getLocationModeSetting(mContext)) .thenReturn(LOCATION_MODE_HIGH_ACCURACY); + try { + when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); + } catch (RemoteException e) { } when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) .thenReturn(false); int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, @@ -799,12 +817,43 @@ public class WifiServiceImplTest { } /** + * Only start LocalOnlyHotspot if the caller is the foreground app at the time of the request. + */ + @Test + public void testStartLocalOnlyHotspotFailsIfRequestorNotForegroundApp() throws Exception { + when(mSettingsStore.getLocationModeSetting(mContext)) + .thenReturn(LOCATION_MODE_HIGH_ACCURACY); + + when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false); + int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, + TEST_PACKAGE_NAME); + assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); + } + + /** + * Do not register the LocalOnlyHotspot request if the caller app cannot be verified as the + * foreground app at the time of the request (ie, throws an exception in the check). + */ + @Test + public void testStartLocalOnlyHotspotFailsIfForegroundAppCheckThrowsRemoteException() + throws Exception { + when(mSettingsStore.getLocationModeSetting(mContext)) + .thenReturn(LOCATION_MODE_HIGH_ACCURACY); + + when(mFrameworkFacade.isAppForeground(anyInt())).thenThrow(new RemoteException()); + int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, + TEST_PACKAGE_NAME); + assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); + } + + /** * Only start LocalOnlyHotspot if we are not tethering. */ @Test - public void testHotspotDoesNotStartWhenAlreadyTethering() { + public void testHotspotDoesNotStartWhenAlreadyTethering() throws Exception { when(mSettingsStore.getLocationModeSetting(mContext)) .thenReturn(LOCATION_MODE_HIGH_ACCURACY); + when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( @@ -816,9 +865,10 @@ public class WifiServiceImplTest { * Only start LocalOnlyHotspot if admin setting does not disallow tethering. */ @Test - public void testHotspotDoesNotStartWhenTetheringDisallowed() { + public void testHotspotDoesNotStartWhenTetheringDisallowed() throws Exception { when(mSettingsStore.getLocationModeSetting(mContext)) .thenReturn(LOCATION_MODE_HIGH_ACCURACY); + when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) .thenReturn(true); int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( @@ -874,7 +924,7 @@ public class WifiServiceImplTest { public void testStopLocalOnlyHotspotTriggersSoftApStopWithOneRegisteredRequest() { registerLOHSRequestFull(); verify(mWifiController) - .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(WifiConfiguration.class)); + .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(SoftApModeConfiguration.class)); mWifiServiceImpl.stopLocalOnlyHotspot(); // there is was only one request registered, we should tear down softap @@ -957,7 +1007,8 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL); + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); Message message = mMessageCaptor.getValue(); @@ -981,7 +1032,8 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL); + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); @@ -1013,7 +1065,8 @@ public class WifiServiceImplTest { reset(mHandler); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); @@ -1045,7 +1098,8 @@ public class WifiServiceImplTest { reset(mHandler); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); @@ -1069,7 +1123,8 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, + IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verifyNoMoreInteractions(mHandler); @@ -1099,9 +1154,11 @@ public class WifiServiceImplTest { reset(mHandler); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); @@ -1125,9 +1182,11 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); @@ -1155,9 +1214,11 @@ public class WifiServiceImplTest { registerLOHSRequestFull(); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); mLooper.dispatchAll(); @@ -1194,9 +1255,11 @@ public class WifiServiceImplTest { reset(mHandler); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); verify(mRequestInfo).sendHotspotStoppedMessage(); mLooper.dispatchAll(); @@ -1223,9 +1286,11 @@ public class WifiServiceImplTest { mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); verify(mRequestInfo2).sendHotspotFailedMessage(ERROR_GENERIC); @@ -1383,9 +1448,11 @@ public class WifiServiceImplTest { // now stop the hotspot TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, - WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, + WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verify(mHandler).handleMessage(mMessageCaptor.capture()); assertEquals(HOTSPOT_STOPPED, mMessageCaptor.getValue().what); @@ -1473,4 +1540,44 @@ public class WifiServiceImplTest { verify(mWifiStateMachine).syncAddOrUpdatePasspointConfig(any(), any(PasspointConfiguration.class), anyInt()); } + + /** + * Verify that a call to {@link WifiServiceImpl#restoreBackupData(byte[])} is only allowed from + * callers with the signature only NETWORK_SETTINGS permission. + */ + @Test(expected = SecurityException.class) + public void testRestoreBackupDataNotApprovedCaller() { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + eq("WifiService")); + mWifiServiceImpl.restoreBackupData(null); + verify(mWifiBackupRestore, never()).retrieveConfigurationsFromBackupData(any(byte[].class)); + } + + /** + * Verify that a call to {@link WifiServiceImpl#restoreSupplicantBackupData(byte[], byte[])} is + * only allowed from callers with the signature only NETWORK_SETTINGS permission. + */ + @Test(expected = SecurityException.class) + public void testRestoreSupplicantBackupDataNotApprovedCaller() { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + eq("WifiService")); + mWifiServiceImpl.restoreSupplicantBackupData(null, null); + verify(mWifiBackupRestore, never()).retrieveConfigurationsFromSupplicantBackupData( + any(byte[].class), any(byte[].class)); + } + + /** + * Verify that a call to {@link WifiServiceImpl#retrieveBackupData()} is only allowed from + * callers with the signature only NETWORK_SETTINGS permission. + */ + @Test(expected = SecurityException.class) + public void testRetrieveBackupDataNotApprovedCaller() { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + eq("WifiService")); + mWifiServiceImpl.retrieveBackupData(); + verify(mWifiBackupRestore, never()).retrieveBackupDataFromConfigurations(any(List.class)); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java index be9b0c544..2a30b671b 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java @@ -16,6 +16,18 @@ package com.android.server.wifi; +import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; + +import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -303,6 +315,7 @@ public class WifiStateMachineTest { static final String sHexSSID = sWifiSsid.getHexString().replace("0x", "").replace("22", ""); static final String sBSSID = "01:02:03:04:05:06"; static final int sFreq = 2437; + static final String WIFI_IFACE_NAME = "mockWlan"; WifiStateMachine mWsm; HandlerThread mWsmThread; @@ -315,6 +328,9 @@ public class WifiStateMachineTest { TestLooper mLooper; Context mContext; + final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor = + ArgumentCaptor.forClass(SoftApManager.Listener.class); + @Mock WifiScanner mWifiScanner; @Mock SupplicantStateTracker mSupplicantStateTracker; @Mock WifiMetrics mWifiMetrics; @@ -370,7 +386,7 @@ public class WifiStateMachineTest { when(mWifiInjector.makeWifiConnectivityManager(any(WifiInfo.class), anyBoolean())) .thenReturn(mWifiConnectivityManager); when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class), - any(SoftApManager.Listener.class), any(IApInterface.class), + mSoftApManagerListenerCaptor.capture(), any(IApInterface.class), any(WifiConfiguration.class))) .thenReturn(mSoftApManager); when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager); @@ -381,7 +397,8 @@ public class WifiStateMachineTest { when(mWifiNative.setupForClientMode()).thenReturn(mClientInterface); when(mWifiNative.setupForSoftApMode()).thenReturn(mApInterface); - when(mWifiNative.getInterfaceName()).thenReturn("mockWlan"); + when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME); + when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME); when(mWifiNative.enableSupplicant()).thenReturn(true); when(mWifiNative.disableSupplicant()).thenReturn(true); when(mWifiNative.getFrameworkNetworkId(anyInt())).thenReturn(0); @@ -478,14 +495,64 @@ public class WifiStateMachineTest { assertEquals("DisconnectedState", getCurrentState().getName()); } - @Test - public void loadComponentsInApMode() throws Exception { - mWsm.setHostApRunning(new WifiConfiguration(), true); + private void checkApStateChangedBroadcast(Intent intent, int expectedCurrentState, + int expectedPrevState, int expectedErrorCode, String expectedIfaceName, + int expectedMode) { + int currentState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); + int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED); + int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON, HOTSPOT_NO_ERROR); + String ifaceName = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME); + int mode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + assertEquals(expectedCurrentState, currentState); + assertEquals(expectedPrevState, prevState); + assertEquals(expectedErrorCode, errorCode); + assertEquals(expectedIfaceName, ifaceName); + assertEquals(expectedMode, mode); + } + + private void loadComponentsInApMode(int mode) throws Exception { + SoftApModeConfiguration config = new SoftApModeConfiguration(mode, new WifiConfiguration()); + mWsm.setHostApRunning(config, true); mLooper.dispatchAll(); assertEquals("SoftApState", getCurrentState().getName()); verify(mSoftApManager).start(); + + // reset expectations for mContext due to previously sent AP broadcast + reset(mContext); + + // get the SoftApManager.Listener and trigger some updates + SoftApManager.Listener listener = mSoftApManagerListenerCaptor.getValue(); + listener.onStateChanged(WIFI_AP_STATE_ENABLING, 0); + listener.onStateChanged(WIFI_AP_STATE_ENABLED, 0); + listener.onStateChanged(WIFI_AP_STATE_DISABLING, 0); + // note, this will trigger a mode change when TestLooper is dispatched + listener.onStateChanged(WIFI_AP_STATE_DISABLED, 0); + + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext, times(4)) + .sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); + + List<Intent> capturedIntents = intentCaptor.getAllValues(); + checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING, + WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode); + checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED, + WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode); + checkApStateChangedBroadcast(capturedIntents.get(2), WIFI_AP_STATE_DISABLING, + WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode); + checkApStateChangedBroadcast(capturedIntents.get(3), WIFI_AP_STATE_DISABLED, + WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode); + } + + @Test + public void loadComponentsInApModeForTethering() throws Exception { + loadComponentsInApMode(WifiManager.IFACE_IP_MODE_TETHERED); + } + + @Test + public void loadComponentsInApModeForLOHS() throws Exception { + loadComponentsInApMode(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } @Test @@ -609,7 +676,9 @@ public class WifiStateMachineTest { mWsm.setSupplicantRunning(false); mWsm.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP); mWsm.sendMessage(WifiMonitor.SUP_DISCONNECTION_EVENT); - mWsm.setHostApRunning(new WifiConfiguration(), true); + SoftApModeConfiguration config = new SoftApModeConfiguration( + WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration()); + mWsm.setHostApRunning(config, true); mLooper.dispatchAll(); assertEquals("SoftApState", getCurrentState().getName()); assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState()); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java index 66a55b51a..34ddf2378 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java @@ -1679,9 +1679,10 @@ public class WifiVendorHalTest { new Random().nextBytes(errorData); // Randomly raise the HIDL callback before we register for the log callback. - // This should be ignored. + // This should be safely ignored. (Not trigger NPE.) mIWifiChipEventCallback.onDebugErrorAlert( errorCode, NativeUtil.byteArrayToArrayList(errorData)); + mLooper.dispatchAll(); WifiNative.WifiLoggerEventHandler eventHandler = mock(WifiNative.WifiLoggerEventHandler.class); @@ -1691,12 +1692,16 @@ public class WifiVendorHalTest { // Now raise the HIDL callback, this should be properly handled. mIWifiChipEventCallback.onDebugErrorAlert( errorCode, NativeUtil.byteArrayToArrayList(errorData)); + mLooper.dispatchAll(); verify(eventHandler).onWifiAlert(eq(errorCode), eq(errorData)); // Now stop the logging and invoke the callback. This should be ignored. + reset(eventHandler); assertTrue(mWifiVendorHal.resetLogHandler()); mIWifiChipEventCallback.onDebugErrorAlert( errorCode, NativeUtil.byteArrayToArrayList(errorData)); + mLooper.dispatchAll(); + verify(eventHandler, never()).onWifiAlert(anyInt(), anyObject()); } /** @@ -1714,9 +1719,10 @@ public class WifiVendorHalTest { new Random().nextBytes(errorData); // Randomly raise the HIDL callback before we register for the log callback. - // This should be ignored. + // This should be safely ignored. (Not trigger NPE.) mIWifiChipEventCallback.onDebugRingBufferDataAvailable( new WifiDebugRingBufferStatus(), NativeUtil.byteArrayToArrayList(errorData)); + mLooper.dispatchAll(); WifiNative.WifiLoggerEventHandler eventHandler = mock(WifiNative.WifiLoggerEventHandler.class); @@ -1726,13 +1732,17 @@ public class WifiVendorHalTest { // Now raise the HIDL callback, this should be properly handled. mIWifiChipEventCallback.onDebugRingBufferDataAvailable( new WifiDebugRingBufferStatus(), NativeUtil.byteArrayToArrayList(errorData)); + mLooper.dispatchAll(); verify(eventHandler).onRingBufferData( any(WifiNative.RingBufferStatus.class), eq(errorData)); // Now stop the logging and invoke the callback. This should be ignored. + reset(eventHandler); assertTrue(mWifiVendorHal.resetLogHandler()); mIWifiChipEventCallback.onDebugRingBufferDataAvailable( new WifiDebugRingBufferStatus(), NativeUtil.byteArrayToArrayList(errorData)); + mLooper.dispatchAll(); + verify(eventHandler, never()).onRingBufferData(anyObject(), anyObject()); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java index d9fbb1d28..e7c5fa962 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java @@ -1257,10 +1257,10 @@ public class WifiScanningServiceTest { } /** - * Verify that the newest full scan results are returned by WifiService.getSingleScanResults. + * Verify that the newest scan results are returned by WifiService.getSingleScanResults. */ @Test - public void retrieveMostRecentFullSingleScanResults() throws Exception { + public void retrieveMostRecentSingleScanResults() throws Exception { WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); ScanResults expectedResults = ScanResults.create(0, true, 2400, 5150, 5175); @@ -1312,64 +1312,6 @@ public class WifiScanningServiceTest { } /** - * Verify that the newest partial scan results are not returned by - * WifiService.getSingleScanResults. - */ - @Test - public void doesNotRetrieveMostRecentPartialSingleScanResults() throws Exception { - WifiScanner.ScanSettings fullRequestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, - 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); - ScanResults expectedFullResults = ScanResults.create(0, true, 2400, 5150, 5175); - doSuccessfulSingleScan(fullRequestSettings, - computeSingleScanNativeSettings(fullRequestSettings), - expectedFullResults); - - Handler handler = mock(Handler.class); - BidirectionalAsyncChannel controlChannel = connectChannel(handler); - InOrder order = inOrder(handler, mWifiScannerImpl); - - controlChannel.sendMessage( - Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); - mLooper.dispatchAll(); - Message response = verifyHandleMessageAndGetMessage(order, handler); - - List<ScanResult> results = Arrays.asList( - ((WifiScanner.ParcelableScanResults) response.obj).getResults()); - assertEquals(results.size(), expectedFullResults.getRawScanResults().length); - - // now update with a new scan that only has one result - int secondScanRequestId = 35; - WifiScanner.ScanSettings partialRequestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, - 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); - ScanResults expectedPartialResults = ScanResults.create(0, false, 5150); - sendSingleScanRequest(controlChannel, secondScanRequestId, partialRequestSettings, null); - - mLooper.dispatchAll(); - WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, - computeSingleScanNativeSettings(partialRequestSettings)); - verifySuccessfulResponse(order, handler, secondScanRequestId); - - // dispatch scan 2 results - when(mWifiScannerImpl.getLatestSingleScanResults()) - .thenReturn(expectedPartialResults.getScanData()); - eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); - - mLooper.dispatchAll(); - verifyScanResultsReceived(order, handler, secondScanRequestId, - expectedPartialResults.getScanData()); - verifySingleScanCompletedReceived(order, handler, secondScanRequestId); - - controlChannel.sendMessage( - Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0)); - mLooper.dispatchAll(); - Message response2 = verifyHandleMessageAndGetMessage(order, handler); - - List<ScanResult> results2 = Arrays.asList( - ((WifiScanner.ParcelableScanResults) response2.obj).getResults()); - assertEquals(results2.size(), expectedFullResults.getRawScanResults().length); - } - - /** * Cached scan results should be cleared after the driver is unloaded. */ @Test |