| /* |
| * Copyright (C) 2016 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 static com.android.server.wifi.WifiSettingsConfigStore.WIFI_DEFAULT_COUNTRY_CODE; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.Context; |
| import android.net.wifi.WifiInfo; |
| import android.os.SystemProperties; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| |
| import com.android.modules.utils.build.SdkLevel; |
| import com.android.server.wifi.util.ApConfigUtil; |
| import com.android.wifi.resources.R; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Provide functions for making changes to WiFi country code. |
| * This Country Code is from MCC or phone default setting. This class sends Country Code |
| * to driver through wpa_supplicant when ClientModeImpl marks current state as ready |
| * using setReadyForChange(true). |
| */ |
| public class WifiCountryCode { |
| private static final String TAG = "WifiCountryCode"; |
| private static final String BOOT_DEFAULT_WIFI_COUNTRY_CODE = "ro.boot.wificountrycode"; |
| private static final int PKT_COUNT_HIGH_PKT_PER_SEC = 16; |
| private static final int DISCONNECT_WIFI_COUNT_MAX = 1; |
| private final String mWorldModeCountryCode; |
| private final Context mContext; |
| private final TelephonyManager mTelephonyManager; |
| private final ActiveModeWarden mActiveModeWarden; |
| private final WifiNative mWifiNative; |
| private final WifiSettingsConfigStore mSettingsConfigStore; |
| private List<ChangeListener> mListeners = new ArrayList<>(); |
| private boolean DBG = false; |
| /** |
| * Map of active ClientModeManager instance to whether it is ready for country code change. |
| * |
| * - When a new ClientModeManager instance is created, it is added to this map and starts out |
| * ready for any country code changes (value = true). |
| * - When the ClientModeManager instance starts a connection attempt, it is marked not ready for |
| * country code changes (value = false). |
| * - When the ClientModeManager instance ends the connection, it is again marked ready for |
| * country code changes (value = true). |
| * - When the ClientModeManager instance is destroyed, it is removed from this map. |
| */ |
| private final Map<ActiveModeManager, Boolean> mAmmToReadyForChangeMap = |
| new ArrayMap<>(); |
| private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); |
| |
| private String mTelephonyCountryCode = null; |
| private String mOverrideCountryCode = null; |
| private String mDriverCountryCode = null; |
| private String mLastActiveDriverCountryCode = null; |
| private long mDriverCountryCodeUpdatedTimestamp = 0; |
| private String mTelephonyCountryTimestamp = null; |
| private String mAllCmmReadyTimestamp = null; |
| private int mDisconnectWifiToForceUpdateCount = 0; |
| |
| private class ModeChangeCallbackInternal implements ActiveModeWarden.ModeChangeCallback { |
| @Override |
| public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { |
| if (activeModeManager.getRole() instanceof ActiveModeManager.ClientRole) { |
| // Add this CMM for tracking. Interface is up and HAL is initialized at this point. |
| // If this device runs the 1.5 HAL version, use the IWifiChip.setCountryCode() |
| // to set the country code. |
| mAmmToReadyForChangeMap.put(activeModeManager, true); |
| evaluateAllCmmStateAndApplyIfAllReady(); |
| } else if (activeModeManager instanceof SoftApManager) { |
| // Put SoftApManager ready for consistence behavior in mAmmToReadyForChangeMap. |
| // No need to trigger CC change because SoftApManager takes CC when starting up. |
| mAmmToReadyForChangeMap.put(activeModeManager, true); |
| } |
| } |
| |
| @Override |
| public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { |
| if (mAmmToReadyForChangeMap.remove(activeModeManager) != null) { |
| if (activeModeManager instanceof ActiveModeManager.ClientRole) { |
| // Remove this CMM from tracking. |
| evaluateAllCmmStateAndApplyIfAllReady(); |
| } |
| } |
| if (mAmmToReadyForChangeMap.size() == 0) { |
| handleCountryCodeChanged(null); |
| Log.i(TAG, "No active mode, call onDriverCountryCodeChanged with Null"); |
| } |
| } |
| |
| @Override |
| public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { |
| if (activeModeManager.getRole() == ActiveModeManager.ROLE_CLIENT_PRIMARY) { |
| // Set this CMM ready for change. This is needed to handle the transition from |
| // ROLE_CLIENT_SCAN_ONLY to ROLE_CLIENT_PRIMARY on devices running older HAL |
| // versions (since the IWifiChip.setCountryCode() was only added in the 1.5 HAL |
| // version, before that we need to wait till supplicant is up for country code |
| // change. |
| mAmmToReadyForChangeMap.put(activeModeManager, true); |
| evaluateAllCmmStateAndApplyIfAllReady(); |
| } |
| } |
| } |
| |
| private class ClientModeListenerInternal implements ClientModeImplListener { |
| @Override |
| public void onConnectionStart(@NonNull ConcreteClientModeManager clientModeManager) { |
| if (mAmmToReadyForChangeMap.get(clientModeManager) == null) { |
| Log.wtf(TAG, "Connection start received from unknown client mode manager"); |
| } |
| // connection start. CMM not ready for country code change. |
| mAmmToReadyForChangeMap.put(clientModeManager, false); |
| evaluateAllCmmStateAndApplyIfAllReady(); |
| } |
| |
| @Override |
| public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) { |
| if (mAmmToReadyForChangeMap.get(clientModeManager) == null) { |
| Log.wtf(TAG, "Connection end received from unknown client mode manager"); |
| } |
| // connection end. CMM ready for country code change. |
| mAmmToReadyForChangeMap.put(clientModeManager, true); |
| evaluateAllCmmStateAndApplyIfAllReady(); |
| } |
| |
| } |
| |
| private class CountryChangeListenerInternal implements ChangeListener { |
| @Override |
| public void onDriverCountryCodeChanged(String country) { |
| if (TextUtils.equals(country, mLastActiveDriverCountryCode)) { |
| return; |
| } |
| Log.i(TAG, "Receive onDriverCountryCodeChanged " + country); |
| if (isDriverSupportedRegChangedEvent()) { |
| // CC doesn't notify listener after sending to the driver, notify the listener |
| // after we received CC changed event. |
| handleCountryCodeChanged(country); |
| } |
| } |
| |
| @Override |
| public void onSetCountryCodeSucceeded(String country) { |
| Log.i(TAG, "Receive onSetCountryCodeSucceeded " + country); |
| // The country code callback might not be triggered even if the driver supports reg |
| // changed event when the maintained country code in the driver is same as set one. |
| // So notify the country code changed event to listener when the set one is same as |
| // last active one. |
| if (!isDriverSupportedRegChangedEvent() |
| || TextUtils.equals(country, mLastActiveDriverCountryCode)) { |
| mWifiNative.countryCodeChanged(country); |
| handleCountryCodeChanged(country); |
| } |
| } |
| } |
| |
| public WifiCountryCode( |
| Context context, |
| ActiveModeWarden activeModeWarden, |
| ClientModeImplMonitor clientModeImplMonitor, |
| WifiNative wifiNative, |
| @NonNull WifiSettingsConfigStore settingsConfigStore) { |
| mContext = context; |
| mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); |
| mActiveModeWarden = activeModeWarden; |
| mWifiNative = wifiNative; |
| mSettingsConfigStore = settingsConfigStore; |
| |
| mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallbackInternal()); |
| clientModeImplMonitor.registerListener(new ClientModeListenerInternal()); |
| mWifiNative.registerCountryCodeEventListener(new CountryChangeListenerInternal()); |
| |
| mWorldModeCountryCode = mContext.getResources() |
| .getString(R.string.config_wifiDriverWorldModeCountryCode); |
| |
| Log.d(TAG, "Default country code from system property " |
| + BOOT_DEFAULT_WIFI_COUNTRY_CODE + " is " + getOemDefaultCountryCode()); |
| } |
| |
| /** |
| * Default country code stored in system property |
| * @return Country code if available, null otherwise. |
| */ |
| public static String getOemDefaultCountryCode() { |
| String country = SystemProperties.get(BOOT_DEFAULT_WIFI_COUNTRY_CODE); |
| return WifiCountryCode.isValid(country) ? country.toUpperCase(Locale.US) : null; |
| } |
| |
| /** |
| * Is this a valid country code |
| * @param countryCode A 2-Character alphanumeric country code. |
| * @return true if the countryCode is valid, false otherwise. |
| */ |
| public static boolean isValid(String countryCode) { |
| return countryCode != null && countryCode.length() == 2 |
| && countryCode.chars().allMatch(Character::isLetterOrDigit); |
| } |
| |
| /** |
| * The class for country code related change listener |
| */ |
| public interface ChangeListener { |
| /** |
| * Called when receiving new country code change pending. |
| */ |
| default void onCountryCodeChangePending(@NonNull String countryCode) {}; |
| |
| /** |
| * Called when receiving country code changed from driver. |
| */ |
| void onDriverCountryCodeChanged(String countryCode); |
| |
| /** |
| * Called when country code set to native layer successful, framework sends event to |
| * force country code changed. |
| * |
| * Reason: The country code change listener from wificond rely on driver supported |
| * NL80211_CMD_REG_CHANGE/NL80211_CMD_WIPHY_REG_CHANGE. Trigger update country code |
| * to listener here for non-supported platform. |
| */ |
| default void onSetCountryCodeSucceeded(String country) {} |
| } |
| |
| |
| /** |
| * Register Country code changed listener. |
| */ |
| public void registerListener(@NonNull ChangeListener listener) { |
| mListeners.add(listener); |
| /** |
| * Always called with mDriverCountryCode even if the SDK version is lower than T. |
| * Reason: Before android S, the purpose of the internal listener is updating the supported |
| * channels, it always depends on mDriverCountryCode. |
| */ |
| if (mDriverCountryCode != null) { |
| listener.onDriverCountryCodeChanged(mDriverCountryCode); |
| } |
| } |
| |
| /** |
| * Enable verbose logging for WifiCountryCode. |
| */ |
| public void enableVerboseLogging(boolean verbose) { |
| DBG = verbose; |
| } |
| |
| private void initializeTelephonyCountryCodeIfNeeded() { |
| // If we don't have telephony country code set yet, poll it. |
| if (mTelephonyCountryCode == null) { |
| Log.d(TAG, "Reading country code from telephony"); |
| setTelephonyCountryCode(mTelephonyManager.getNetworkCountryIso()); |
| } |
| } |
| |
| /** |
| * We call native code to request country code changes only if all {@link ClientModeManager} |
| * instances are ready for country code change. Country code is a chip level configuration and |
| * results in all the connections on the chip being disrupted. |
| * |
| * @return true if there are active CMM's and all are ready for country code change. |
| */ |
| private boolean isAllCmmReady() { |
| boolean isAnyCmmExist = false; |
| for (ActiveModeManager am : mAmmToReadyForChangeMap.keySet()) { |
| if (am instanceof ConcreteClientModeManager) { |
| isAnyCmmExist = true; |
| if (!mAmmToReadyForChangeMap.get(am)) { |
| return false; |
| } |
| } |
| } |
| return isAnyCmmExist; |
| } |
| |
| /** |
| * Check all active CMM instances and apply country code change if ready. |
| */ |
| private void evaluateAllCmmStateAndApplyIfAllReady() { |
| Log.d(TAG, "evaluateAllCmmStateAndApplyIfAllReady: " + mAmmToReadyForChangeMap); |
| if (isAllCmmReady()) { |
| mAllCmmReadyTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); |
| // We are ready to set country code now. |
| // We need to post pending country code request. |
| initializeTelephonyCountryCodeIfNeeded(); |
| updateCountryCode(true); |
| } |
| } |
| |
| /** |
| * This call will override any existing country code. |
| * This is for test purpose only and we should disallow any update from |
| * telephony in this mode. |
| * @param countryCode A 2-Character alphanumeric country code. |
| */ |
| public synchronized void setOverrideCountryCode(String countryCode) { |
| if (TextUtils.isEmpty(countryCode)) { |
| Log.d(TAG, "Fail to override country code because" |
| + "the received country code is empty"); |
| return; |
| } |
| // Support 00 map to device world mode country code |
| if (TextUtils.equals("00", countryCode)) { |
| countryCode = mWorldModeCountryCode; |
| } |
| mOverrideCountryCode = countryCode.toUpperCase(Locale.US); |
| updateCountryCode(false); |
| } |
| |
| /** |
| * This is for clearing the country code previously set through #setOverrideCountryCode() method |
| */ |
| public synchronized void clearOverrideCountryCode() { |
| mOverrideCountryCode = null; |
| updateCountryCode(false); |
| } |
| |
| private void setTelephonyCountryCode(String countryCode) { |
| Log.d(TAG, "Set telephony country code to: " + countryCode); |
| mTelephonyCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); |
| |
| // Empty country code. |
| if (TextUtils.isEmpty(countryCode)) { |
| if (mContext.getResources() |
| .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss)) { |
| Log.d(TAG, "Received empty country code, reset to default country code"); |
| mTelephonyCountryCode = null; |
| } |
| } else { |
| mTelephonyCountryCode = countryCode.toUpperCase(Locale.US); |
| } |
| } |
| |
| /** |
| * Handle country code change request. |
| * @param countryCode The country code intended to set. |
| * This is supposed to be from Telephony service. |
| * otherwise we think it is from other applications. |
| * @return Returns true if the country code passed in is acceptable and passed to the driver. |
| */ |
| public boolean setTelephonyCountryCodeAndUpdate(String countryCode) { |
| if (TextUtils.isEmpty(countryCode) |
| && !TextUtils.isEmpty(mTelephonyManager.getNetworkCountryIso())) { |
| Log.i(TAG, "Skip Telephony CC update to empty because there is " |
| + "an available CC from default active SIM"); |
| return false; |
| } |
| // We do not check if the country code (CC) equals the current one because |
| // 1. Wpa supplicant may silently modify the country code. |
| // 2. If Wifi restarted therefore wpa_supplicant also restarted, |
| setTelephonyCountryCode(countryCode); |
| if (mOverrideCountryCode != null) { |
| Log.d(TAG, "Skip Telephony CC update due to override country code set"); |
| return false; |
| } |
| |
| updateCountryCode(false); |
| return true; |
| } |
| |
| private void disconnectWifiToForceUpdateIfNeeded() { |
| if (shouldDisconnectWifiToForceUpdate()) { |
| Log.d(TAG, "Disconnect wifi to force update"); |
| for (ClientModeManager cmm : |
| mActiveModeWarden.getInternetConnectivityClientModeManagers()) { |
| if (!cmm.isConnected()) { |
| continue; |
| } |
| cmm.disconnect(); |
| } |
| mDisconnectWifiToForceUpdateCount++; |
| } |
| } |
| |
| private boolean shouldDisconnectWifiToForceUpdate() { |
| if (mTelephonyCountryCode == null |
| || mTelephonyCountryCode.equals(mDriverCountryCode)) { |
| return false; |
| } |
| |
| if (mDisconnectWifiToForceUpdateCount >= DISCONNECT_WIFI_COUNT_MAX) { |
| return false; |
| } |
| |
| if (mDriverCountryCode != null |
| && !mDriverCountryCode.equalsIgnoreCase(mWorldModeCountryCode)) { |
| return false; |
| } |
| |
| for (ClientModeManager cmm : |
| mActiveModeWarden.getInternetConnectivityClientModeManagers()) { |
| if (!cmm.isConnected()) { |
| continue; |
| } |
| WifiInfo wifiInfo = cmm.syncRequestConnectionInfo(); |
| if (wifiInfo.getSuccessfulTxPacketsPerSecond() < PKT_COUNT_HIGH_PKT_PER_SEC |
| && wifiInfo.getSuccessfulRxPacketsPerSecond() < PKT_COUNT_HIGH_PKT_PER_SEC) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Method to get the received driver Country Code that being used in driver. |
| * |
| * @return Returns the local copy of the received driver Country Code or null if |
| * there is no Country Code was received from driver or no any active mode. |
| */ |
| @Nullable |
| public synchronized String getCurrentDriverCountryCode() { |
| return mDriverCountryCode; |
| } |
| |
| /** |
| * Method to return the currently reported Country Code resolved from various sources: |
| * e.g. default country code, cellular network country code, country code override, etc. |
| * |
| * @return The current Wifi Country Code resolved from various sources. Returns null when there |
| * is no Country Code available. |
| */ |
| @Nullable |
| public synchronized String getCountryCode() { |
| initializeTelephonyCountryCodeIfNeeded(); |
| return pickCountryCode(); |
| } |
| |
| /** |
| * set default country code |
| * @param countryCode A 2-Character alphanumeric country code. |
| */ |
| public synchronized void setDefaultCountryCode(String countryCode) { |
| if (TextUtils.isEmpty(countryCode)) { |
| Log.d(TAG, "Fail to set default country code because the country code is empty"); |
| return; |
| } |
| |
| mSettingsConfigStore.put(WIFI_DEFAULT_COUNTRY_CODE, |
| countryCode.toUpperCase(Locale.US)); |
| Log.i(TAG, "Default country code updated in config store: " + countryCode); |
| updateCountryCode(false); |
| } |
| |
| /** |
| * Method to dump the current state of this WifiCountryCode object. |
| */ |
| public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| pw.println("mRevertCountryCodeOnCellularLoss: " |
| + mContext.getResources().getBoolean( |
| R.bool.config_wifi_revert_country_code_on_cellular_loss)); |
| pw.println("DefaultCountryCode(system property): " + getOemDefaultCountryCode()); |
| pw.println("DefaultCountryCode(config store): " |
| + mSettingsConfigStore.get(WIFI_DEFAULT_COUNTRY_CODE)); |
| pw.println("mTelephonyCountryCode: " + mTelephonyCountryCode); |
| pw.println("mTelephonyCountryTimestamp: " + mTelephonyCountryTimestamp); |
| pw.println("mOverrideCountryCode: " + mOverrideCountryCode); |
| pw.println("mAllCmmReadyTimestamp: " + mAllCmmReadyTimestamp); |
| pw.println("isAllCmmReady: " + isAllCmmReady()); |
| pw.println("mAmmToReadyForChangeMap: " + mAmmToReadyForChangeMap); |
| pw.println("mDisconnectWifiToForceUpdateCount: " + mDisconnectWifiToForceUpdateCount); |
| pw.println("mDriverCountryCode: " + mDriverCountryCode); |
| pw.println("mDriverCountryCodeUpdatedTimestamp: " |
| + (mDriverCountryCodeUpdatedTimestamp != 0 |
| ? FORMATTER.format(new Date(mDriverCountryCodeUpdatedTimestamp)) : "N/A")); |
| pw.println("isDriverSupportedRegChangedEvent: " |
| + isDriverSupportedRegChangedEvent()); |
| } |
| |
| private boolean isDriverSupportedRegChangedEvent() { |
| return mContext.getResources().getBoolean( |
| R.bool.config_wifiDriverSupportedNl80211RegChangedEvent); |
| } |
| |
| private void updateCountryCode(boolean isClientModeOnly) { |
| String country = pickCountryCode(); |
| Log.d(TAG, "updateCountryCode to " + country); |
| |
| // We do not check if the country code equals the current one. |
| // There are two reasons: |
| // 1. Wpa supplicant may silently modify the country code. |
| // 2. If Wifi restarted therefore wpa_supplicant also restarted, |
| // the country code could be reset to '00' by wpa_supplicant. |
| if (country != null) { |
| setCountryCodeNative(country, isClientModeOnly); |
| } |
| // We do not set country code if there is no candidate. This is reasonable |
| // because wpa_supplicant usually starts with an international safe country |
| // code setting: '00'. |
| } |
| |
| private String pickCountryCode() { |
| if (mOverrideCountryCode != null) { |
| return mOverrideCountryCode; |
| } |
| if (mTelephonyCountryCode != null) { |
| return mTelephonyCountryCode; |
| } |
| if (mDriverCountryCode != null) { |
| // Returns driver country code since it may be different to WIFI_DEFAULT_COUNTRY_CODE |
| // when driver supported 802.11d. |
| return mDriverCountryCode; |
| } |
| return mSettingsConfigStore.get(WIFI_DEFAULT_COUNTRY_CODE); |
| } |
| |
| private boolean setCountryCodeNative(String country, boolean isClientModeOnly) { |
| Set<ActiveModeManager> amms = mAmmToReadyForChangeMap.keySet(); |
| boolean isConcreteClientModeManagerUpdated = false; |
| boolean anyAmmConfigured = false; |
| boolean isAllCmmReady = isAllCmmReady(); |
| if (!isAllCmmReady) { |
| Log.d(TAG, "skip update supplicant not ready yet"); |
| disconnectWifiToForceUpdateIfNeeded(); |
| } |
| Log.d(TAG, "setCountryCodeNative" + country + ", isClientModeOnly" + isClientModeOnly); |
| for (ActiveModeManager am : amms) { |
| if (isAllCmmReady && !isConcreteClientModeManagerUpdated |
| && am instanceof ConcreteClientModeManager) { |
| // Set the country code using one of the active mode managers. Since |
| // country code is a chip level global setting, it can be set as long |
| // as there is at least one active interface to communicate to Wifi chip |
| ConcreteClientModeManager cm = (ConcreteClientModeManager) am; |
| if (!cm.setCountryCode(country)) { |
| Log.d(TAG, "Failed to set country code (ConcreteClientModeManager) to " |
| + country); |
| } else { |
| isConcreteClientModeManagerUpdated = true; |
| anyAmmConfigured = true; |
| // Start from S, frameworks support country code callback from wificond, |
| // move "notify the lister" to CountryChangeListenerInternal. |
| if (!SdkLevel.isAtLeastS() && !isDriverSupportedRegChangedEvent()) { |
| handleCountryCodeChanged(country); |
| } |
| } |
| } else if (!isClientModeOnly && am instanceof SoftApManager) { |
| SoftApManager sm = (SoftApManager) am; |
| if (mDriverCountryCode == null || TextUtils.equals(mDriverCountryCode, country)) { |
| // Ignore SoftApManager init country code case or country code didn't be |
| // changed case. |
| continue; |
| } |
| // Restart SAP only when 1. overlay enabled 2. CC is not world mode. |
| if (ApConfigUtil.isSoftApRestartRequiredWhenCountryCodeChanged(mContext) |
| && !mDriverCountryCode.equalsIgnoreCase(mWorldModeCountryCode)) { |
| Log.i(TAG, "restart SoftAp required because country code changed to " |
| + country); |
| SoftApModeConfiguration modeConfig = sm.getSoftApModeConfiguration(); |
| mActiveModeWarden.stopSoftAp(modeConfig.getTargetMode()); |
| mActiveModeWarden.startSoftAp(modeConfig, sm.getRequestorWs()); |
| } else { |
| // The API:updateCountryCode in SoftApManager is asynchronous, it requires a |
| // new callback support in S to trigger "notifyListener" for |
| // the new S API: SoftApCapability#getSupportedChannelList(band). |
| // It requires: |
| // 1. a new overlay configuration which is introduced from S. |
| // 2. wificond support in S for S API: SoftApCapability#getSupportedChannelList |
| // Any case if device supported to set country code in R, |
| // the new S API: SoftApCapability#getSupportedChannelList(band) still doesn't |
| // work normally in R build when wifi disabled. |
| if (!sm.updateCountryCode(country)) { |
| Log.d(TAG, "Can't set country code (SoftApManager) to " |
| + country + " when SAP on (Device doesn't support runtime update)"); |
| } else { |
| anyAmmConfigured = true; |
| } |
| } |
| } |
| } |
| if (!anyAmmConfigured) { |
| for (ChangeListener listener : mListeners) { |
| if (country != null) { |
| listener.onCountryCodeChangePending(country); |
| } |
| } |
| } |
| return anyAmmConfigured; |
| } |
| |
| private void handleCountryCodeChanged(String country) { |
| if (!TextUtils.equals(mDriverCountryCode, country)) { |
| mDriverCountryCodeUpdatedTimestamp = System.currentTimeMillis(); |
| mDriverCountryCode = country; |
| if (country != null) { |
| mLastActiveDriverCountryCode = country; |
| } |
| notifyListener(country); |
| } |
| } |
| |
| |
| /** |
| * Notify the listeners. There are two kind of listeners |
| * 1. external listener, they only care what is country code which driver is using now. |
| * 2. internal listener, frameworks also only care what is country code which driver is using |
| * now because it requires to update supported channels with new country code. |
| * |
| * Note: Call this API only after confirming the CC is used in driver. |
| * |
| * @param country the country code is used in driver or null when driver is non-active. |
| */ |
| private void notifyListener(@Nullable String country) { |
| mActiveModeWarden.updateClientScanModeAfterCountryCodeUpdate(country); |
| for (ChangeListener listener : mListeners) { |
| listener.onDriverCountryCodeChanged(country); |
| } |
| } |
| } |