diff options
25 files changed, 2458 insertions, 142 deletions
diff --git a/service/Android.mk b/service/Android.mk index 8201acff9..cdee104a4 100644 --- a/service/Android.mk +++ b/service/Android.mk @@ -89,6 +89,8 @@ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(call include-path-for, libhardware)/hardware \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ + external/icu/icu4c/source/common \ + external/icu/icu4c/source/i18n \ libcore/include LOCAL_SHARED_LIBRARIES += \ @@ -98,6 +100,8 @@ LOCAL_SHARED_LIBRARIES += \ libhardware \ libhardware_legacy \ libnl \ + libicuuc \ + libicui18n \ libdl LOCAL_STATIC_LIBRARIES += libwifi-hal-stub @@ -105,6 +109,7 @@ LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL) LOCAL_SRC_FILES := \ jni/com_android_server_wifi_WifiNative.cpp \ + jni/com_android_server_wifi_Gbk2Utf.cpp \ jni/jni_helper.cpp ifdef INCLUDE_NAN_FEATURE diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java index 2dfb75447..98dad2bec 100644 --- a/service/java/com/android/server/wifi/SoftApManager.java +++ b/service/java/com/android/server/wifi/SoftApManager.java @@ -35,6 +35,7 @@ import com.android.server.wifi.util.ApConfigUtil; import java.util.ArrayList; import java.util.Locale; +import android.os.UserHandle; /** * Manage WiFi in AP mode. @@ -49,7 +50,10 @@ public class SoftApManager { private final String mCountryCode; - private final String mInterfaceName; + private String mInterfaceName; + private boolean mCreateNewInterface = false; + private int mSoftApChannel = 0; + private String mTetherInterfaceName; private final SoftApStateMachine mStateMachine; @@ -111,6 +115,23 @@ public class SoftApManager { } /** + * Set SoftAp channel + * @param channel is channel number + */ + public void setSapChannel(int channel) { + mSoftApChannel = channel; + } + + /** + * Set SoftAp interfcae name + * @param name name of the interface + */ + public void setSapInterfaceName(String name) { + mInterfaceName = name; + mCreateNewInterface = true; + } + + /** * Start a soft AP instance with the given configuration. * @param config AP configuration * @return integer result code @@ -132,7 +153,7 @@ public class SoftApManager { } /* Setup country code if it is provide. */ - if (mCountryCode != null) { + if (mCountryCode != null && (mCountryCode.length() != 0)) { /** * Country code is mandatory for 5GHz band, return an error if failed to set * country code when AP is configured for 5GHz band. @@ -146,6 +167,14 @@ public class SoftApManager { } try { + if (mCreateNewInterface) { + mNmService.createSoftApInterface(mInterfaceName); + if ((localConfig.apBand != WifiConfiguration.AP_BAND_5GHZ) + && (mSoftApChannel != 0)) { + localConfig.apBand = WifiConfiguration.AP_BAND_2GHZ; + localConfig.apChannel = mSoftApChannel; + } + } mNmService.startAccessPoint(localConfig, mInterfaceName); } catch (Exception e) { Log.e(TAG, "Exception in starting soft AP: " + e); @@ -163,6 +192,9 @@ public class SoftApManager { private void stopSoftAp() { try { mNmService.stopAccessPoint(mInterfaceName); + if (mCreateNewInterface) { + mNmService.deleteSoftApInterface(mInterfaceName); + } } catch (Exception e) { Log.e(TAG, "Exception in stopping soft AP: " + e); return; diff --git a/service/java/com/android/server/wifi/SoftApStateMachine.java b/service/java/com/android/server/wifi/SoftApStateMachine.java new file mode 100644 index 000000000..1b21bb60c --- /dev/null +++ b/service/java/com/android/server/wifi/SoftApStateMachine.java @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2010 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 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 android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; +import static android.system.OsConstants.ARPHRD_ETHER; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.dhcp.DhcpClient; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConnectionStatistics; +import android.net.wifi.WifiManager; +import android.os.BatteryStats; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.R; +import com.android.internal.app.IBatteryStats; +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.MessageUtils; +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Track the state of Wifi connectivity. All event handling is done here, + * and all changes in connectivity state are initiated here. + * + * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p + * In the current implementation, we support concurrent wifi p2p and wifi operation. + * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService + * handles p2p operation. + * + * @hide + */ +public class SoftApStateMachine extends StateMachine { + + private static boolean DBG = false; + private static final String TAG = "SoftApStateMachine"; + + private WifiMonitor mWifiMonitor; + private WifiNative mWifiNative; + private WifiConfigManager mWifiConfigManager; + private INetworkManagementService mNwService; + private ConnectivityManager mCm; + private BaseWifiLogger mWifiLogger; + private WifiApConfigStore mWifiApConfigStore; + private final Clock mClock = new Clock(); + private final WifiCountryCode mCountryCode; + + private String mInterfaceName = "softap0"; + private int mSoftApChannel = 0; + /* Tethering interface could be separate from wlan interface */ + private String mTetherInterfaceName; + private int mCurrentUserId = UserHandle.USER_SYSTEM; + private WifiStateMachine mWifiStateMachine = null; + + /** + * Tether state change notification time out + */ + private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000; + + /* Tracks sequence number on a tether notification time out */ + private int mTetherToken = 0; + + /*Wakelock held during wifi start/stop and driver load/unload */ + private PowerManager.WakeLock mWakeLock; + + private Context mContext; + /* The base for wifi message types */ + static final int BASE = Protocol.BASE_WIFI; + /* Start the soft ap */ + static final int CMD_START_AP = BASE + 21; + /* Indicates soft ap start failed */ + static final int CMD_START_AP_FAILURE = BASE + 22; + /* Stop the soft ap */ + static final int CMD_STOP_AP = BASE + 23; + /* Soft access point teardown is completed. */ + static final int CMD_AP_STOPPED = BASE + 24; + + public static final int CMD_BOOT_COMPLETED = BASE + 134; + + + /** + * One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + */ + private final AtomicInteger mWifiApState + = new AtomicInteger(WIFI_AP_STATE_DISABLED); + private final IBatteryStats mBatteryStats; + + /* Temporary initial state */ + private State mInitialState = new InitialState(); + /* Soft ap state */ + private State mSoftApState = new SoftApState(); + + private FrameworkFacade mFacade; + private final BackupManagerProxy mBackupManagerProxy; + + + public SoftApStateMachine(Context context, WifiStateMachine wifiStateMachine, + FrameworkFacade facade,String intf, + WifiConfigManager configManager, + WifiMonitor wifiMonitor, + BackupManagerProxy backupManagerProxy, + INetworkManagementService NwService, + IBatteryStats BatteryStats, + WifiCountryCode countryCode) { + super("SoftApStateMachine"); + + mContext = context; + mWifiStateMachine = wifiStateMachine; + mFacade = facade; + mWifiNative = WifiNative.getWlanNativeInterface(); + mWifiNative.initContext(mContext); + mInterfaceName = intf; + mBackupManagerProxy = backupManagerProxy; + mNwService = NwService; + mBatteryStats = BatteryStats; + mWifiConfigManager = configManager; + mWifiMonitor = wifiMonitor; + mCountryCode = countryCode; + + addState(mInitialState); + addState(mSoftApState, mInitialState); + setInitialState(mInitialState); + start(); + + } + + void enableVerboseLogging(int verbose) { + if (verbose > 0) { + DBG = true; + } else { + DBG = false; + } + } + + public void setSoftApInterfaceName(String iface) { + mInterfaceName = iface; + } + + public void setSoftApChannel(int channel) { + mSoftApChannel = channel; + } + + + /* Leverage from WiFiStateMachine */ + public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) { + if (enable) { + sendMessage(CMD_START_AP, wifiConfig); + } else { + sendMessage(CMD_STOP_AP); + } + } + + public void setWifiApConfiguration(WifiConfiguration config) { + mWifiApConfigStore.setApConfiguration(config); + } + + /* Leverage from WiFiStateMachine */ + public WifiConfiguration syncGetWifiApConfiguration() { + return mWifiApConfigStore.getApConfiguration(); + } + + /* Leverage from WiFiStateMachine */ + public int syncGetWifiApState() { + return mWifiApState.get(); + } + + /* Leverage from WiFiStateMachine */ + public String syncGetWifiApStateByName() { + switch (mWifiApState.get()) { + case WIFI_AP_STATE_DISABLING: + return "disabling"; + case WIFI_AP_STATE_DISABLED: + return "disabled"; + case WIFI_AP_STATE_ENABLING: + return "enabling"; + case WIFI_AP_STATE_ENABLED: + return "enabled"; + case WIFI_AP_STATE_FAILED: + return "failed"; + default: + return "[invalid state]"; + } + } + + /* Leverage from WiFiStateMachine */ + private void setWifiApState(int wifiApState, int reason) { + final int previousWifiApState = mWifiApState.get(); + + try { + if (wifiApState == WIFI_AP_STATE_ENABLED) { + mBatteryStats.noteWifiOn(); + } else if (wifiApState == WIFI_AP_STATE_DISABLED) { + mBatteryStats.noteWifiOff(); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + + // Update state + mWifiApState.set(wifiApState); + + if (DBG) Log.d(TAG,"setWifiApState: " + syncGetWifiApStateByName()); + + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); + if (wifiApState == WifiManager.WIFI_AP_STATE_FAILED) { + //only set reason number when softAP start failed + intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason); + } + + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + /* Leverage from WiFiStateMachine */ + private boolean setupDriverForSoftAp() { + if (!mWifiNative.loadDriver()) { + Log.e(TAG, "Failed to load driver for softap"); + return false; + } + if (mWifiStateMachine != null) { + int wifiState = mWifiStateMachine.syncGetWifiState(); + if ((wifiState == WifiManager.WIFI_STATE_ENABLING) || + (wifiState == WifiManager.WIFI_STATE_ENABLED)) { + Log.d(TAG,"Wifi is in enabled state skip firmware reload"); + return true; + } + } + + try { + mNwService.wifiFirmwareReload(mInterfaceName, "AP"); + if (DBG) Log.d(TAG, "Firmware reloaded in AP mode"); + } catch (Exception e) { + Log.e(TAG, "Failed to reload AP firmware " + e); + } + if (!mWifiNative.startHal()) { + Log.e(TAG, "Failed to start HAL"); + } + return true; + } + + /* Leverage from WiFiStateMachine */ + private void checkAndSetConnectivityInstance() { + if (mCm == null) { + mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + } + } + + /* + * SoftApStateMAchine states + * (InitialState) + * | + * | + * V + * (SoftApState) + * + * InitialState : By default control sits in this state after + * SoftApStateMachine is getting initialized. + * It unload wlan driver if it is loaded. + * On request turn on softap, it movies to + * SoftApState. + * SoftApState : Once driver and firmware successfully loaded + * it control sits in this state. + * On request to stop softap, it movies back to + * InitialState. + * + */ + + /* Leverage from WiFiStateMachine */ + class InitialState extends State { + @Override + public void enter() { + boolean skipUnload = false; + if (mWifiStateMachine != null) { + int wifiState = mWifiStateMachine.syncGetWifiState(); + if ((wifiState == WifiManager.WIFI_STATE_ENABLING) || + (wifiState == WifiManager.WIFI_STATE_ENABLED)) { + Log.d(TAG, "Avoid unload driver, WIFI_STATE is enabled/enabling"); + skipUnload = true; + } + } + if (!skipUnload) { + mWifiNative.stopHal(); + mWifiNative.unloadDriver(); + } + if (mWifiApConfigStore == null) { + mWifiApConfigStore = + mFacade.makeApConfigStore(mContext, mBackupManagerProxy); + } + } + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_START_AP: + if (setupDriverForSoftAp()) { + transitionTo(mSoftApState); + } else { + setWifiApState(WIFI_AP_STATE_FAILED, + WifiManager.SAP_START_FAILURE_GENERAL); + /** + * Transition to InitialState (current state) to reset the + * driver/HAL back to the initial state. + */ + transitionTo(mInitialState); + } + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + /* Leverage from WiFiStateMachine */ + class SoftApState extends State { + private SoftApManager mSoftApManager; + + private class SoftApListener implements SoftApManager.Listener { + @Override + public void onStateChanged(int state, int reason) { + if (state == WIFI_AP_STATE_DISABLED) { + sendMessage(CMD_AP_STOPPED); + } else if (state == WIFI_AP_STATE_FAILED) { + sendMessage(CMD_START_AP_FAILURE); + } + + setWifiApState(state, reason); + } + } + + @Override + public void enter() { + final Message message = getCurrentMessage(); + if (message.what == CMD_START_AP) { + WifiConfiguration config = (WifiConfiguration) message.obj; + + if (config == null) { + /** + * Configuration not provided in the command, fallback to use the current + * configuration. + */ + config = mWifiApConfigStore.getApConfiguration(); + } else { + /* Update AP configuration. */ + mWifiApConfigStore.setApConfiguration(config); + } + + checkAndSetConnectivityInstance(); + mSoftApManager = mFacade.makeSoftApManager( + mContext, getHandler().getLooper(), mWifiNative, mNwService, + mCm, mCountryCode.getCurrentCountryCode(), + mWifiApConfigStore.getAllowed2GChannel(), + new SoftApListener()); + if (mSoftApChannel != 0) { + mSoftApManager.setSapChannel(mSoftApChannel); + } + mSoftApManager.setSapInterfaceName(mInterfaceName); + mSoftApManager.start(config); + } else { + throw new RuntimeException("Illegal transition to SoftApState: " + message); + } + } + + @Override + public void exit() { + mSoftApManager = null; + } + + @Override + public boolean processMessage(Message message) { + + switch(message.what) { + case CMD_START_AP: + /* Ignore start command when it is starting/started. */ + break; + case CMD_STOP_AP: + mSoftApManager.stop(); + break; + case CMD_START_AP_FAILURE: + transitionTo(mInitialState); + break; + case CMD_AP_STOPPED: + transitionTo(mInitialState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + /** + * arg2 on the source message has a unique id that needs to be retained in replies + * to match the request + * <p>see WifiManager for details + */ + private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) { + Message msg = Message.obtain(); + msg.what = what; + msg.arg2 = srcMsg.arg2; + return msg; + } +} diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index bcd8d03e9..2f49586d5 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -20,6 +20,10 @@ import android.content.Context; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.text.TextUtils; import android.util.Log; import com.android.internal.R; @@ -54,6 +58,8 @@ public class WifiApConfigStore { private final String mApConfigFile; private final BackupManagerProxy mBackupManagerProxy; + private static boolean mEnableRegionalHotspotCheckbox = false; + WifiApConfigStore(Context context, BackupManagerProxy backupManagerProxy) { this(context, backupManagerProxy, DEFAULT_AP_CONFIG_FILE); } @@ -87,6 +93,11 @@ public class WifiApConfigStore { /* Save the default configuration to persistent storage. */ writeApConfiguration(mApConfigFile, mWifiApConfig); } + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_regional_hotspot_show_broadcast_ssid_checkbox + )) { + mEnableRegionalHotspotCheckbox = true; + } } /** @@ -135,6 +146,9 @@ public class WifiApConfigStore { return null; } config.SSID = in.readUTF(); + if (mEnableRegionalHotspotCheckbox) { + config.hiddenSSID = (in.readInt() != 0); + } if (version >= 2) { config.apBand = in.readInt(); @@ -146,6 +160,10 @@ public class WifiApConfigStore { if (authType != KeyMgmt.NONE) { config.preSharedKey = in.readUTF(); } + // read in wifiApInactivityTimeout if bytes are available from in + if (in.available() != 0) { + config.wifiApInactivityTimeout = in.readLong(); + } } catch (IOException e) { Log.e(TAG, "Error reading hotspot configuration " + e); config = null; @@ -170,6 +188,9 @@ public class WifiApConfigStore { new FileOutputStream(filename)))) { out.writeInt(AP_CONFIG_FILE_VERSION); out.writeUTF(config.SSID); + if (mEnableRegionalHotspotCheckbox) { + out.writeInt(config.hiddenSSID ? 1 : 0); + } out.writeInt(config.apBand); out.writeInt(config.apChannel); int authType = config.getAuthType(); @@ -177,6 +198,7 @@ public class WifiApConfigStore { if (authType != KeyMgmt.NONE) { out.writeUTF(config.preSharedKey); } + out.writeLong(config.wifiApInactivityTimeout); } catch (IOException e) { Log.e(TAG, "Error writing hotspot configuration" + e); } @@ -192,10 +214,17 @@ public class WifiApConfigStore { WifiConfiguration config = new WifiConfiguration(); config.SSID = mContext.getResources().getString( R.string.wifi_tether_configure_ssid_default); - config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); - String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13); + int wifiApSecurityType = mContext.getResources().getInteger( + R.integer.wifi_hotspot_security_type); + config.allowedKeyManagement.set(wifiApSecurityType); + config.preSharedKey = mContext.getResources().getString( + R.string.def_wifi_wifihotspot_pass); + if (TextUtils.isEmpty(config.preSharedKey)) { + String randomUUID = UUID.randomUUID().toString(); + //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13); + } + config.wifiApInactivityTimeout = 0; return config; } } diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 5eff02ee6..9129fe612 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -392,10 +392,20 @@ public class WifiConfigManager { R.bool.config_wifi_fast_bss_transition_enabled); boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled); - Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled")); + boolean hs2onSet = (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.WIFI_HOTSPOT2_ENABLED, 0) == 1); + Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + + (hs2on ? "enabled" : "disabled") + ", " + hs2onSet); mConfiguredNetworks = new ConfigurationMap(userManager); - mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on); + + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_passpoint_setting_on)) { + mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2onSet); + } else { + mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on); + } + mEnableOsuQueries = true; mAnqpCache = new AnqpCache(mClock); mSupplicantBridgeCallbacks = new SupplicantBridgeCallbacks(); @@ -413,6 +423,23 @@ public class WifiConfigManager { mAnqpCache.clear(all, DBG); } + public HashSet<Integer> getConfiguredChannelList() { + /* Hashset will avoid any duplicate frequency to be added in hashmap */ + HashSet<Integer> freqs = new HashSet<Integer>(); + for(WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { + if (getScanDetailCache(config) != null) { + for(ScanDetail scanDetail : getScanDetailCache(config).values()) { + ScanResult result = scanDetail.getScanResult(); + freqs.add(result.frequency); + } + } + } + if (freqs.isEmpty()) + return null; + else + return freqs; + } + void enableVerboseLogging(int verbose) { mEnableVerboseLogging.set(verbose); if (verbose > 0) { @@ -1683,6 +1710,7 @@ public class WifiConfigManager { final Map<String, WifiConfiguration> configs = new HashMap<>(); final SparseArray<Map<String, String>> networkExtras = new SparseArray<>(); + mScanDetailCaches.clear(); mLastPriority = mWifiConfigStore.loadNetworks(configs, networkExtras); readNetworkHistory(configs); diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index e4aef24c6..fc4c122fa 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -283,6 +283,15 @@ public class WifiConfigStore { config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null); } + value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.SIMNumVarName); + if (!TextUtils.isEmpty(value)) { + try { + config.SIMNum = Integer.parseInt(value); + } catch (NumberFormatException ignore) { + Log.e(TAG,"error in parsing Selected Sim number " + config.SIMNum); + } + } + value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName); config.priority = -1; if (!TextUtils.isEmpty(value)) { @@ -737,6 +746,13 @@ public class WifiConfigStore { return false; } } + if (config.SIMNum != 0 && !mWifiNative.setNetworkVariable( + netId, + WifiConfiguration.SIMNumVarName, + Integer.toString(config.SIMNum))) { + loge(config.SIMNum + ": failed to set sim no: " + config.SIMNum); + return false; + } if (!mWifiNative.setNetworkVariable( netId, WifiConfiguration.priorityVarName, diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index 49a2842d6..1ca2608cb 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -164,6 +164,9 @@ public class WifiConnectivityManager { // A helper to log debugging information in the local log buffer, which can // be retrieved in bugreport. private void localLog(String log) { + if (mDbg) { + Log.d(TAG, log); + } mLocalLog.log(log); } @@ -179,7 +182,7 @@ public class WifiConnectivityManager { // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times // if the start scan command failed. An timer is used here to make it a deferred retry. private class RestartSingleScanListener implements AlarmManager.OnAlarmListener { - private final boolean mIsFullBandScan; + private boolean mIsFullBandScan; RestartSingleScanListener(boolean isFullBandScan) { mIsFullBandScan = isFullBandScan; @@ -187,6 +190,9 @@ public class WifiConnectivityManager { @Override public void onAlarm() { + if (mStateMachine.getScanCount() < mStateMachine.getMaxConfiguredScanCount()) { + mIsFullBandScan = false; + } startSingleScan(mIsFullBandScan); } } @@ -392,6 +398,17 @@ public class WifiConnectivityManager { @Override public void onSuccess() { localLog("SingleScanListener onSuccess"); + /* As part of optimizing time for initial scans for + * saved profiles, increment the scan trigger count + * upon receiving a success. + */ + int mScanCount = 0; + mScanCount = mStateMachine.getScanCount(); + if (mScanCount < mStateMachine.getMaxConfiguredScanCount()) { + mStateMachine.setScanCount(++mScanCount); + } + // reset the count + mSingleScanRestartCount = 0; } @Override @@ -681,6 +698,19 @@ public class WifiConnectivityManager { } } + private boolean populateFreqList(ScanSettings settings) { + Set<Integer> freqs = mConfigManager.getConfiguredChannelList(); + if (freqs != null && freqs.size() != 0) { + int index = 0; + settings.channels = new WifiScanner.ChannelSpec[freqs.size()]; + for (Integer freq : freqs) { + settings.channels[index++] = new WifiScanner.ChannelSpec(freq); + } + return true; + } + return false; + } + // Helper for setting the channels for connectivity scan when band is unspecified. Returns // false if we can't retrieve the info. private boolean setScanChannels(ScanSettings settings) { @@ -749,6 +779,9 @@ public class WifiConnectivityManager { } mLastPeriodicSingleScanTimeStamp = currentTimeStamp; + if (mStateMachine.getScanCount() < mStateMachine.getMaxConfiguredScanCount()) { + isFullBandScan = false; + } startSingleScan(isFullBandScan); schedulePeriodicScanTimer(mPeriodicSingleScanInterval); @@ -785,7 +818,11 @@ public class WifiConnectivityManager { ScanSettings settings = new ScanSettings(); if (!isFullBandScan) { - if (!setScanChannels(settings)) { + if (mStateMachine.getScanCount() < mStateMachine.getMaxConfiguredScanCount()) { + if (!populateFreqList(settings)) { + isFullBandScan = true; + } + } else if (!setScanChannels(settings)) { isFullBandScan = true; } } diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index 67a7a423e..26f662adb 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -53,7 +53,7 @@ import java.io.PrintWriter; */ public class WifiController extends StateMachine { private static final String TAG = "WifiController"; - private static final boolean DBG = false; + private static boolean DBG = false; private Context mContext; private boolean mScreenOff; private boolean mDeviceIdle; @@ -62,6 +62,7 @@ public class WifiController extends StateMachine { private long mIdleMillis; private int mSleepPolicy; private boolean mFirstUserSignOnSeen = false; + private boolean mStaAndApConcurrency = false; private AlarmManager mAlarmManager; private PendingIntent mIdleIntent; @@ -93,8 +94,9 @@ public class WifiController extends StateMachine { "com.android.server.WifiManager.action.DEVICE_IDLE"; /* References to values tracked in WifiService */ - private final WifiStateMachine mWifiStateMachine; - private final WifiSettingsStore mSettingsStore; + final WifiStateMachine mWifiStateMachine; + private SoftApStateMachine mSoftApStateMachine = null; + final WifiSettingsStore mSettingsStore; private final WifiLockManager mWifiLockManager; /** @@ -135,7 +137,9 @@ public class WifiController extends StateMachine { private ApStaDisabledState mApStaDisabledState = new ApStaDisabledState(); private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState(); private ApEnabledState mApEnabledState = new ApEnabledState(); + private ApStaEnabledState mApStaEnabledState = new ApStaEnabledState(); private DeviceActiveState mDeviceActiveState = new DeviceActiveState(); + private DeviceActiveHighPerfState mDeviceActiveHighPerfState = new DeviceActiveHighPerfState(); private DeviceInactiveState mDeviceInactiveState = new DeviceInactiveState(); private ScanOnlyLockHeldState mScanOnlyLockHeldState = new ScanOnlyLockHeldState(); private FullLockHeldState mFullLockHeldState = new FullLockHeldState(); @@ -159,7 +163,9 @@ public class WifiController extends StateMachine { addState(mDefaultState); addState(mApStaDisabledState, mDefaultState); addState(mStaEnabledState, mDefaultState); + addState(mApStaEnabledState, mDefaultState); addState(mDeviceActiveState, mStaEnabledState); + addState(mDeviceActiveHighPerfState, mDeviceActiveState); addState(mDeviceInactiveState, mStaEnabledState); addState(mScanOnlyLockHeldState, mDeviceInactiveState); addState(mFullLockHeldState, mDeviceInactiveState); @@ -349,6 +355,20 @@ public class WifiController extends StateMachine { mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource); } + public void setSoftApStateMachine(SoftApStateMachine machine) { + mSoftApStateMachine = machine; + mStaAndApConcurrency = true; + Slog.d(TAG, "mStaAndApConcurrency="+mStaAndApConcurrency); + } + + void enableVerboseLogging(int verbose) { + if (verbose > 0) { + DBG = true; + } else { + DBG = false; + } + } + class DefaultState extends State { @Override public boolean processMessage(Message msg) { @@ -458,7 +478,7 @@ public class WifiController extends StateMachine { break; } if (mDeviceIdle == false) { - transitionTo(mDeviceActiveState); + checkLocksAndTransitionWhenDeviceActive(); } else { checkLocksAndTransitionWhenDeviceIdle(); } @@ -476,8 +496,13 @@ 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, - true); + if (mStaAndApConcurrency) { + mSoftApStateMachine.setHostApRunning((WifiConfiguration) msg.obj, + true); + } else { + mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj, + true); + } transitionTo(mApEnabledState); } break; @@ -517,6 +542,90 @@ public class WifiController extends StateMachine { } + class ApStaEnabledState extends State { + private State mPendingState = null; + private int mDeferredEnableSerialNumber = 0; + private boolean mHaveDeferredEnable = false; + private long mDisabledTimestamp; + + @Override + public void enter() { + if (DBG) { + Slog.d(TAG,"ApStaEnabledState enter"); + } + } + + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case CMD_SET_AP: + if (msg.arg1 == 1) { + if (DBG) { + Slog.d(TAG,"ApStaEnabledState CMD_SET_AP setHostApRunning true"); + } + mSoftApStateMachine.setHostApRunning((WifiConfiguration) msg.obj, + true); + } else { + if (DBG) { + Slog.d(TAG,"ApStaEnabledState CMD_SET_AP setHostApRunning false"); + } + mSoftApStateMachine.setHostApRunning(null, false); + transitionTo(mStaEnabledState); + } + break; + case CMD_WIFI_TOGGLED: + if (mSettingsStore.isWifiToggleEnabled()) { + if (doDeferEnable(msg)) { + if (mHaveDeferredEnable) { + // have 2 toggles now, inc serial number an ignore both + mDeferredEnableSerialNumber++; + } + mHaveDeferredEnable = !mHaveDeferredEnable; + break; + } + if (DBG) { + Slog.d(TAG,"ApStaEnabledState CMD_WIFI_TOGGLED setSupplicantRunning true"); + } + mWifiStateMachine.setSupplicantRunning(true); + } else { + if (DBG) { + Slog.d(TAG,"ApStaEnabledState CMD_WIFI_TOGGLED setSupplicantRunning false"); + } + mWifiStateMachine.setSupplicantRunning(false); + transitionTo(mApEnabledState); + } + break; + case CMD_AIRPLANE_TOGGLED: + mSoftApStateMachine.setHostApRunning(null, false); + mPendingState = mApStaDisabledState; + break; + case CMD_AP_STOPPED: + if(mPendingState != null) { + transitionTo(mPendingState); + } + } + return HANDLED; + } + + private boolean doDeferEnable(Message msg) { + long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; + if (delaySoFar >= mReEnableDelayMillis) { + return false; + } + + log("WifiController msg " + msg + " deferred for " + + (mReEnableDelayMillis - delaySoFar) + "ms"); + + // need to defer this action. + Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); + deferredMsg.obj = Message.obtain(msg); + deferredMsg.arg1 = ++mDeferredEnableSerialNumber; + sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); + return true; + } + + } + class StaEnabledState extends State { @Override public void enter() { @@ -563,7 +672,11 @@ public class WifiController extends StateMachine { // remeber that we were enabled mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj)); - transitionTo(mApStaDisabledState); + if (mStaAndApConcurrency) { + transitionTo(mApStaEnabledState); + } else { + transitionTo(mApStaDisabledState); + } } break; default: @@ -607,7 +720,7 @@ public class WifiController extends StateMachine { break; } if (mDeviceIdle == false) { - transitionTo(mDeviceActiveState); + checkLocksAndTransitionWhenDeviceActive(); } else { checkLocksAndTransitionWhenDeviceIdle(); } @@ -683,7 +796,9 @@ public class WifiController extends StateMachine { */ private State getNextWifiState() { if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) { - return mDeviceActiveState; + if (!mStaAndApConcurrency) { + return mDeviceActiveState; + } } if (mSettingsStore.isScanAlwaysAvailable()) { @@ -698,20 +813,36 @@ public class WifiController extends StateMachine { switch (msg.what) { case CMD_AIRPLANE_TOGGLED: if (mSettingsStore.isAirplaneModeOn()) { - mWifiStateMachine.setHostApRunning(null, false); + if (mStaAndApConcurrency) { + mSoftApStateMachine.setHostApRunning(null, false); + } else { + mWifiStateMachine.setHostApRunning(null, false); + } mPendingState = mApStaDisabledState; } break; case CMD_WIFI_TOGGLED: if (mSettingsStore.isWifiToggleEnabled()) { - mWifiStateMachine.setHostApRunning(null, false); - mPendingState = mDeviceActiveState; + if (mStaAndApConcurrency) { + deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj)); + if (DBG) { + Slog.d(TAG,"ApEnabledState CMD_WIFI_TOGGLED transition To ApStaEnabledState"); + } + transitionTo(mApStaEnabledState); + } else { + mWifiStateMachine.setHostApRunning(null, false); + mPendingState = mStaEnabledState; + } } break; case CMD_SET_AP: if (msg.arg1 == 0) { - mWifiStateMachine.setHostApRunning(null, false); - mPendingState = getNextWifiState(); + if (mStaAndApConcurrency) { + mSoftApStateMachine.setHostApRunning(null, false); + } else { + mWifiStateMachine.setHostApRunning(null, false); + mPendingState = getNextWifiState(); + } } break; case CMD_AP_STOPPED: @@ -733,7 +864,11 @@ public class WifiController extends StateMachine { case CMD_EMERGENCY_CALL_STATE_CHANGED: case CMD_EMERGENCY_MODE_CHANGED: if (msg.arg1 == 1) { - mWifiStateMachine.setHostApRunning(null, false); + if (mStaAndApConcurrency) { + mSoftApStateMachine.setHostApRunning(null, false); + } else { + mWifiStateMachine.setHostApRunning(null, false); + } mPendingState = mEcmState; } break; @@ -799,7 +934,7 @@ public class WifiController extends StateMachine { if (exitEcm) { if (mSettingsStore.isWifiToggleEnabled()) { if (mDeviceIdle == false) { - transitionTo(mDeviceActiveState); + checkLocksAndTransitionWhenDeviceActive(); } else { checkLocksAndTransitionWhenDeviceIdle(); } @@ -826,6 +961,9 @@ public class WifiController extends StateMachine { if (msg.what == CMD_DEVICE_IDLE) { checkLocksAndTransitionWhenDeviceIdle(); // We let default state handle the rest of work + } else if (msg.what == CMD_LOCKS_CHANGED) { + checkLocksAndTransitionWhenDeviceActive(); + return HANDLED; } else if (msg.what == CMD_USER_PRESENT) { // TLS networks can't connect until user unlocks keystore. KeyStore // unlocks when the user punches PIN after the reboot. So use this @@ -844,6 +982,16 @@ public class WifiController extends StateMachine { } } + /* Parent: DeviceActiveState. Device is active, and an app is holding a high perf lock. */ + class DeviceActiveHighPerfState extends State { + @Override + public void enter() { + mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); + mWifiStateMachine.setDriverStart(true); + mWifiStateMachine.setHighPerfModeEnabled(true); + } + } + /* Parent: StaEnabledState */ class DeviceInactiveState extends State { @Override @@ -854,7 +1002,7 @@ public class WifiController extends StateMachine { updateBatteryWorkSource(); return HANDLED; case CMD_SCREEN_ON: - transitionTo(mDeviceActiveState); + checkLocksAndTransitionWhenDeviceActive(); // More work in default state return NOT_HANDLED; default: @@ -900,6 +1048,17 @@ public class WifiController extends StateMachine { } } + private void checkLocksAndTransitionWhenDeviceActive() { + if (mWifiLockManager.getStrongestLockMode() == WIFI_MODE_FULL_HIGH_PERF) { + // It is possible for the screen to be off while the device is + // is active (mIdleMillis), so we need the high-perf mode + // otherwise powersaving mode will be turned on. + transitionTo(mDeviceActiveHighPerfState); + } else { + transitionTo(mDeviceActiveState); + } + } + private void checkLocksAndTransitionWhenDeviceIdle() { switch (mWifiLockManager.getStrongestLockMode()) { case WIFI_MODE_NO_LOCKS_HELD: diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java index 13396562a..a6974776b 100644 --- a/service/java/com/android/server/wifi/WifiCountryCode.java +++ b/service/java/com/android/server/wifi/WifiCountryCode.java @@ -160,6 +160,11 @@ public class WifiCountryCode { * country code. * Returns null if no Country Code was sent to driver. */ + public synchronized String getCurrentCountryCode() { + mCurrentCountryCode = pickCountryCode(); + return mCurrentCountryCode; + } + public synchronized String getCountryCodeSentToDriver() { return mCurrentCountryCode; } diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 6583db242..6cf87fef0 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -933,8 +933,9 @@ public class WifiMetrics { */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mLock) { - if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) { - // Dump serialized WifiLog proto + pw.println("WifiMetrics:"); + if ((args != null) && args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) { + //Dump serialized WifiLog proto consolidateProto(true); for (ConnectionEvent event : mConnectionEventList) { if (mCurrentConnectionEvent != event) { diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index 1f2b39713..d9366e766 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -81,6 +81,10 @@ public class WifiMonitor { private static final int UNKNOWN = 14; private static final int SCAN_FAILED = 15; + /** Vendor defined events from supplicant daemon */ + private static final int WIFI_VENDOR_EVENT_BASE = 255; + private static final int SUBNET_STATUS_UPDATE = WIFI_VENDOR_EVENT_BASE + 1; + /** All events coming from the supplicant start with this prefix */ private static final String EVENT_PREFIX_STR = "CTRL-EVENT-"; private static final int EVENT_PREFIX_LEN_STR = EVENT_PREFIX_STR.length(); @@ -429,6 +433,12 @@ public class WifiMonitor { */ private static final String P2P_SERV_DISC_RESP_STR = "P2P-SERV-DISC-RESP"; + /* P2P-REMOVE-AND-REFORM-GROUP */ + /* Supplicant is supposed to generate this event only when p2p + * is connected + */ + private static final String P2P_REMOVE_AND_REFORM_GROUP_STR = "P2P-REMOVE-AND-REFORM-GROUP"; + private static final String HOST_AP_EVENT_PREFIX_STR = "AP"; /* AP-STA-CONNECTED 42:fc:89:a8:96:09 dev_addr=02:90:4c:a0:92:54 */ private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED"; @@ -437,8 +447,12 @@ public class WifiMonitor { private static final String ANQP_DONE_STR = "ANQP-QUERY-DONE"; private static final String HS20_ICON_STR = "RX-HS20-ICON"; + /* WPA_EVENT_SUBNET_STATUS_UPDATE status=0|1|2 */ + private static final String SUBNET_STATUS_UPDATE_STR ="SUBNET-STATUS-UPDATE"; + /* Supplicant events reported to a state machine */ private static final int BASE = Protocol.BASE_WIFI_MONITOR; + private static final int VENDOR_BASE_WIFI_MONITOR = 255; /* Connection to supplicant established */ public static final int SUP_CONNECTION_EVENT = BASE + 1; @@ -497,6 +511,7 @@ public class WifiMonitor { public static final int P2P_FIND_STOPPED_EVENT = BASE + 37; public static final int P2P_SERV_DISC_RESP_EVENT = BASE + 38; public static final int P2P_PROV_DISC_FAILURE_EVENT = BASE + 39; + public static final int P2P_REMOVE_AND_REFORM_GROUP_EVENT = BASE + 40; /* hostap events */ public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41; @@ -514,6 +529,9 @@ public class WifiMonitor { /* hotspot 2.0 events */ public static final int HS20_REMEDIATION_EVENT = BASE + 61; + /* subnet status change event */ + public static final int SUBNET_STATUS_UPDATE_EVENT = VENDOR_BASE_WIFI_MONITOR + 62; + /** * This indicates a read error on the monitor socket conenction */ @@ -597,9 +615,9 @@ public class WifiMonitor { new MonitorThread(mWifiNative.getLocalLog()).start(); return true; } - if (connectTries++ < 5) { + if (connectTries++ < 50) { try { - Thread.sleep(1000); + Thread.sleep(100); } catch (InterruptedException ignore) { } } else { @@ -763,11 +781,17 @@ public class WifiMonitor { int space = eventStr.indexOf(' '); if (space != -1) { iface = eventStr.substring(7, space); - if (!mHandlerMap.containsKey(iface) && iface.startsWith("p2p-")) { - // p2p interfaces are created dynamically, but we have - // only one P2p state machine monitoring all of them; look - // for it explicitly, and send messages there .. - iface = "p2p0"; + if (!mHandlerMap.containsKey(iface)) { + if (iface.startsWith("p2p-")) { + // p2p interfaces are created dynamically, but we have + // only one P2p state machine monitoring all of them; look + // for it explicitly, and send messages there .. + iface = "p2p0"; + } else { + Log.i(TAG, "Ignoring event from unexpected interface: " + + eventStr); + return false; + } } eventStr = eventStr.substring(space + 1); } else { @@ -812,7 +836,10 @@ public class WifiMonitor { } if (!eventStr.startsWith(EVENT_PREFIX_STR)) { - if (eventStr.startsWith(WPS_SUCCESS_STR)) { + if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) && + 0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) { + sendMessage(iface, AUTHENTICATION_FAILURE_EVENT); + } else if (eventStr.startsWith(WPS_SUCCESS_STR)) { sendMessage(iface, WPS_SUCCESS_EVENT); } else if (eventStr.startsWith(WPS_FAIL_STR)) { handleWpsFailEvent(eventStr, iface); @@ -921,6 +948,8 @@ public class WifiMonitor { event = BSS_ADDED; } else if (eventName.equals(BSS_REMOVED_STR)) { event = BSS_REMOVED; + } else if (eventName.equals(SUBNET_STATUS_UPDATE_STR)) { + event = SUBNET_STATUS_UPDATE; } else event = UNKNOWN; @@ -1066,6 +1095,10 @@ public class WifiMonitor { sendMessage(iface, SCAN_FAILED_EVENT); break; + case SUBNET_STATUS_UPDATE: + handleSupplicantVendorDebugEvent(iface, remainder); + break; + case UNKNOWN: if (DBG) { Log.w(TAG, "handleEvent unknown: " + Integer.toString(event) + " " + remainder); @@ -1221,6 +1254,9 @@ public class WifiMonitor { } else { Log.e(TAG, "Null service resp " + dataString); } + } else if (dataString.startsWith(P2P_REMOVE_AND_REFORM_GROUP_STR)) { + Log.d(TAG, "Received event= " + dataString); + sendMessage(iface, P2P_REMOVE_AND_REFORM_GROUP_EVENT); } } @@ -1451,4 +1487,47 @@ public class WifiMonitor { sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, BSSID); } } + + /** + * Send subnet change status to state machine + */ + private void notifyIpSubnetStatusChange(String iface, int subnetStatus) { + /* valid values for subnet status are: + * 0 = unknown, 1 = unchanged, 2 = changed + */ + if (subnetStatus < 0 || subnetStatus > 2) { + Log.e(TAG, "Invalid IP subnet status: " + subnetStatus); + return; + } + + sendMessage(iface, SUBNET_STATUS_UPDATE_EVENT, subnetStatus); + } + + /** + * Handle vendor specific events from the supplicant + */ + private void handleSupplicantVendorDebugEvent(String iface, String eventStr) { + int subnetStatus = 0; + + if (DBG) Log.w(TAG, "IP subnet status change event - " + eventStr); + + String[] tokens = eventStr.split(" "); + if (tokens.length < 2) { + Log.e(TAG, "IP subnet status event: Invalid tokens"); + return; + } + String[] nameValue = tokens[1].split("="); + if (nameValue.length != 2) { + Log.e(TAG, "IP subnet status event: Invalid nameValue"); + return; + } + + try { + subnetStatus = Integer.parseInt(nameValue[1]); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + notifyIpSubnetStatusChange(iface, subnetStatus); + } } diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 13876f324..f6931c1fe 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -898,6 +898,17 @@ public class WifiNative { doBooleanCommand("SET update_config 1"); } + public void enableTdlsExtControl() { + doBooleanCommand("SET tdls_external_control 1"); + } + + public void disableScanOffload() { + doBooleanCommand("SET disable_scan_offload 1"); + } + + public void setP2pDisable() { + doBooleanCommand("SET p2p_disabled 1"); + } public boolean saveConfig() { return doBooleanCommand("SAVE_CONFIG"); } @@ -1538,6 +1549,8 @@ public class WifiNative { } } + public native static boolean setApMode(boolean enable); + /* WIFI HAL support */ // HAL command ids diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java index edbc51649..5b285f5f3 100644 --- a/service/java/com/android/server/wifi/WifiNetworkHistory.java +++ b/service/java/com/android/server/wifi/WifiNetworkHistory.java @@ -333,6 +333,8 @@ public class WifiNetworkHistory { String bssid = null; String ssid = null; + String key = null; + String value = null; int freq = 0; int status = 0; @@ -347,12 +349,16 @@ public class WifiNetworkHistory { break; } int colon = line.indexOf(':'); - if (colon < 0) { + char slash = line.charAt(0); + if ((colon < 0)&& (slash != '/')) { continue; } - - String key = line.substring(0, colon).trim(); - String value = line.substring(colon + 1).trim(); + if (slash == '/') { + key = line.trim(); + } else { + key = line.substring(0, colon).trim(); + value = line.substring(colon + 1).trim(); + } if (key.equals(CONFIG_KEY)) { config = configs.get(value); @@ -486,8 +492,15 @@ public class WifiNetworkHistory { break; case BSSID_KEY: status = 0; - ssid = null; - bssid = null; + /* + * The intention here is to put the scanDetail in to + * the scanDetailCache per config , as done in + * BSSID_KEY_END . Thus store bssid value and + * comment ssid = null to ensure the code in the if + * loop is executed for the case BSSID_KEY_END. + */ + // ssid = null; + bssid = value; freq = 0; seen = 0; rssi = WifiConfiguration.INVALID_RSSI; diff --git a/service/java/com/android/server/wifi/WifiNotificationController.java b/service/java/com/android/server/wifi/WifiNotificationController.java index 6df2eb81e..413e4c8e5 100644 --- a/service/java/com/android/server/wifi/WifiNotificationController.java +++ b/service/java/com/android/server/wifi/WifiNotificationController.java @@ -174,7 +174,8 @@ final class WifiNotificationController { //A capability of [ESS] represents an open access point //that is available for an STA to connect if (scanResult.capabilities != null && - scanResult.capabilities.equals("[ESS]")) { + (scanResult.capabilities.equals("[ESS]") || + scanResult.capabilities.equals("[WPS][ESS]"))) { numOpenNetworks++; } } diff --git a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java index 1e7560346..e8968d5d0 100644 --- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java @@ -64,8 +64,9 @@ public class WifiQualifiedNetworkSelector { private WifiNetworkScoreCache mNetworkScoreCache; private Clock mClock; private static final String TAG = "WifiQualifiedNetworkSelector:"; + private boolean skipQualifiedNetworkSelectionForAutoConnect = false; // Always enable debugging logs for now since QNS is still a new feature. - private static final boolean FORCE_DEBUG = true; + private static final boolean FORCE_DEBUG = false; private boolean mDbg = FORCE_DEBUG; private WifiConfiguration mCurrentConnectedNetwork = null; private String mCurrentBssid = null; @@ -137,6 +138,7 @@ public class WifiQualifiedNetworkSelector { private void localLog(String log) { if (mDbg) { + Log.d(TAG, log); mLocalLog.log(log); } } @@ -323,8 +325,10 @@ public class WifiQualifiedNetworkSelector { return false; } + int currentBand = mWifiInfo.is24GHz() ? + WifiManager.WIFI_FREQUENCY_BAND_2GHZ : WifiManager.WIFI_FREQUENCY_BAND_5GHZ; // Current network band must match with user preference selection - if (mWifiInfo.is24GHz() && (mUserPreferedBand != WifiManager.WIFI_FREQUENCY_BAND_2GHZ)) { + if (currentBand != mUserPreferedBand) { localLog("Current band does not match user preference. Start Qualified Network" + " Selection Current band = " + (mWifiInfo.is24GHz() ? "2.4GHz band" : "5GHz band") + "UserPreference band = " + mUserPreferedBand); @@ -344,6 +348,9 @@ public class WifiQualifiedNetworkSelector { return true; } + public void skipQualifiedNetworkSelectionForAutoConnect(boolean enable) { + skipQualifiedNetworkSelectionForAutoConnect = enable; + } /** * check whether QualifiedNetworkSelection is needed or not * @@ -370,6 +377,10 @@ public class WifiQualifiedNetworkSelector { localLog("Need not Qualified Network Selection during L2 debouncing"); return false; } + if (skipQualifiedNetworkSelectionForAutoConnect) { + localLog("Skip network selction, since auto connection disabled"); + return false; + } if (isConnected) { //already connected. Just try to find better candidate @@ -427,11 +438,15 @@ public class WifiQualifiedNetworkSelector { WifiConfiguration currentNetwork, boolean sameBssid, boolean sameSelect, StringBuffer sbuf) { - int score = 0; - //calculate the RSSI score - int rssi = scanResult.level <= mWifiConfigManager.mThresholdSaturatedRssi24.get() - ? scanResult.level : mWifiConfigManager.mThresholdSaturatedRssi24.get(); - score += (rssi + mRssiScoreOffset) * mRssiScoreSlope; + // get the band-specific saturation threshold + int saturationThreshold = scanResult.is24GHz() ? + mWifiConfigManager.mThresholdSaturatedRssi24.get() : + mWifiConfigManager.mThresholdSaturatedRssi5.get(); + + // calculate the RSSI score, capped by the saturation threshold + int rssi = Math.min(scanResult.level, saturationThreshold); + + int score = (rssi + mRssiScoreOffset) * mRssiScoreSlope; sbuf.append(" RSSI score: " + score); if (scanResult.is5GHz()) { //5GHz band diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index ae42db0de..bea194908 100644..100755 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -30,6 +30,7 @@ import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; import android.Manifest; import android.app.ActivityManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.app.AppOpsManager; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; @@ -64,6 +65,7 @@ import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -96,7 +98,15 @@ import com.android.server.wifi.configparse.ConfigBuilder; import org.xml.sax.SAXException; import java.io.BufferedReader; +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.nio.channels.FileChannel; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; @@ -114,6 +124,8 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import android.app.AppOpsManager; +import android.os.SystemProperties; /** * WifiService handles remote WiFi operation requests by implementing @@ -125,6 +137,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { private static final String TAG = "WifiService"; private static final boolean DBG = true; private static final boolean VDBG = false; + private boolean mIsFactoryResetOn = false; + private boolean mSubSystemRestart = false; private static final String BOOT_DEFAULT_WIFI_COUNTRY_CODE = "ro.boot.wificountrycode"; final WifiStateMachine mWifiStateMachine; @@ -132,6 +146,19 @@ public class WifiServiceImpl extends IWifiManager.Stub { private final Context mContext; private final FrameworkFacade mFacade; + private SoftApStateMachine mSoftApStateMachine; + private int mStaAndApConcurrency = 0; + private String mSoftApInterfaceName = null; + private int mSoftApChannel = 0; + private static final String SEPARATOR_KEY = "\n"; + private static final String ENABLE_STA_SAP + = "ENABLE_STA_SAP_CONCURRENCY:"; + private static final String SAP_INTERFACE_NAME + = "SAP_INTERFACE_NAME:"; + private static final String SAP_CHANNEL + = "SAP_CHANNEL:"; + private static final String mConcurrencyCfgTemplateFile = + "/etc/wifi/wifi_concurrency_cfg.txt"; private final List<Multicaster> mMulticasters = new ArrayList<Multicaster>(); private int mMulticastEnabled; @@ -157,6 +184,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { private final WifiCertManager mCertManager; private final WifiInjector mWifiInjector; + private boolean mIsControllerStarted = false; /** * Asynchronous channel to WifiStateMachine */ @@ -350,6 +378,20 @@ public class WifiServiceImpl extends IWifiManager.Stub { mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore, mWifiLockManager, wifiThread.getLooper(), mFacade); + if (ensureConcurrencyFileExist()) { + readConcurrencyConfig(); + } + if (mStaAndApConcurrency == 1) { + mWifiStateMachine.setStaSoftApConcurrency(); + mSoftApStateMachine = mWifiStateMachine.getSoftApStateMachine(); + if (mSoftApInterfaceName != null) { + mSoftApStateMachine.setSoftApInterfaceName(mSoftApInterfaceName); + } + if (mSoftApChannel != 0) { + mSoftApStateMachine.setSoftApChannel(mSoftApChannel); + } + mWifiController.setSoftApStateMachine(mSoftApStateMachine); + } // Set the WifiController for WifiLastResortWatchdog mWifiInjector.getWifiLastResortWatchdog().setWifiController(mWifiController); @@ -413,6 +455,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { mInIdleMode = mPowerManager.isDeviceIdleMode(); mWifiController.start(); + mIsControllerStarted = true; // If we are already disabled (could be due to airplane mode), avoid changing persist // state here @@ -555,6 +598,10 @@ public class WifiServiceImpl extends IWifiManager.Stub { "ConnectivityService"); } + private boolean isStrictOpEnable() { + return SystemProperties.getBoolean("persist.sys.strict_op_enable", false); + } + /** * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} * @param enable {@code true} to enable, {@code false} to disable. @@ -566,7 +613,15 @@ public class WifiServiceImpl extends IWifiManager.Stub { enforceChangePermission(); Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); - + if(isStrictOpEnable()){ + if((Binder.getCallingUid() > 10000) && (packageName.indexOf("android.uid.systemui") !=0) && (packageName.indexOf("android.uid.system") != 0)) { + AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + int result = mAppOpsManager.noteOp(AppOpsManager.OP_CHANGE_WIFI_STATE,Binder.getCallingUid(),packageName); + if(result == AppOpsManager.MODE_IGNORED){ + return false; + } + } + } /* * Caller might not have WRITE_SECURE_SETTINGS, * only CHANGE_WIFI_STATE is enforced @@ -581,6 +636,10 @@ public class WifiServiceImpl extends IWifiManager.Stub { Binder.restoreCallingIdentity(ident); } + if (!mIsControllerStarted) { + Slog.e(TAG,"WifiController is not yet started, abort setWifiEnabled"); + return false; + } if (mPermissionReviewRequired) { final int wiFiEnabledState = getWifiEnabledState(); @@ -1236,7 +1295,10 @@ public class WifiServiceImpl extends IWifiManager.Stub { if (dhcpResults.ipAddress != null && dhcpResults.ipAddress.getAddress() instanceof Inet4Address) { - info.ipAddress = NetworkUtils.inetAddressToInt((Inet4Address) dhcpResults.ipAddress.getAddress()); + info.ipAddress = NetworkUtils.inetAddressToInt( + (Inet4Address) dhcpResults.ipAddress.getAddress()); + info.netmask = NetworkUtils.prefixLengthToNetmaskInt( + dhcpResults.ipAddress.getNetworkPrefixLength()); } if (dhcpResults.gateway != null) { @@ -1429,6 +1491,54 @@ public class WifiServiceImpl extends IWifiManager.Stub { mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0, 0); } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { handleIdleModeChanged(); + } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN); + if (state == WifiManager.WIFI_STATE_ENABLED) { + if (mIsFactoryResetOn) { + resetWifiNetworks(); + mIsFactoryResetOn = false; + } + if (mSubSystemRestart) { + setWifiApEnabled(null, true); + } + } else if ( state == WifiManager.WIFI_STATE_DISABLED) { + if (mSubSystemRestart) { + try { + setWifiEnabled(mContext.getPackageName(), true); + } catch (RemoteException e) { + /* ignore - local call */ + } + } + } + } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { + int wifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, + WifiManager.WIFI_AP_STATE_FAILED); + if (mSubSystemRestart) { + if (wifiApState == WifiManager.WIFI_AP_STATE_DISABLED) { + if (getWifiEnabledState() == WifiManager.WIFI_STATE_ENABLED) { + try { + setWifiEnabled(mContext.getPackageName(), false); + } catch (RemoteException e) { + /* ignore - local call */ + } + } else { + /** + * STA in DISABLED state, hence just restart SAP. + * This should cover two scenarios + * 1. Only SAP ON ( before SSR ) in STA + SAP. + * 2. No STA + SAP. + */ + setWifiApEnabled(null, true); + mSubSystemRestart = false; + } + } else if (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED) { + mSubSystemRestart = false; + } + } + + } else if (action.equals(WifiManager.WIFI_AP_SUB_SYSTEM_RESTART)) { + handleSubSystemRestart(); } } }; @@ -1485,10 +1595,12 @@ public class WifiServiceImpl extends IWifiManager.Stub { intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); - + intentFilter.addAction(WifiManager.WIFI_AP_SUB_SYSTEM_RESTART); + intentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); boolean trackEmergencyCallState = mContext.getResources().getBoolean( com.android.internal.R.bool.config_wifi_turn_off_during_emergency_call); if (trackEmergencyCallState) { @@ -1748,6 +1860,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public void enableVerboseLogging(int verbose) { enforceAccessPermission(); mWifiStateMachine.enableVerboseLogging(verbose); + mWifiController.enableVerboseLogging(verbose); mWifiLockManager.enableVerboseLogging(verbose); } @@ -1798,6 +1911,21 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } + public boolean getWifiStaSapConcurrency() { + return mStaAndApConcurrency == 1; + } + + private void resetWifiNetworks() { + // Delete all Wifi SSIDs + List<WifiConfiguration> networks = getConfiguredNetworks(); + if (networks != null) { + for (WifiConfiguration config : networks) { + removeNetwork(config.networkId); + } + saveConfiguration(); + } + } + public void factoryReset() { enforceConnectivityInternalPermission(); @@ -1811,19 +1939,16 @@ public class WifiServiceImpl extends IWifiManager.Stub { } if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) { - // Enable wifi - try { - setWifiEnabled(mContext.getOpPackageName(), true); - } catch (RemoteException e) { - /* ignore - local call */ - } - // Delete all Wifi SSIDs - List<WifiConfiguration> networks = getConfiguredNetworks(); - if (networks != null) { - for (WifiConfiguration config : networks) { - removeNetwork(config.networkId); + if (getWifiEnabledState() == WifiManager.WIFI_STATE_ENABLED) { + resetWifiNetworks(); + } else { + mIsFactoryResetOn = true; + // Enable wifi + try { + setWifiEnabled(mContext.getOpPackageName(), true); + } catch (RemoteException e) { + /* ignore - local call */ } - saveConfiguration(); } } } @@ -1975,4 +2100,101 @@ public class WifiServiceImpl extends IWifiManager.Stub { enforceConnectivityInternalPermission(); mWifiStateMachine.enableWifiConnectivityManager(enabled); } + + private void readConcurrencyConfig() { + BufferedReader reader = null; + try { + if (mConcurrencyCfgTemplateFile != null) { + Log.d(TAG, "mConcurrencyCfgTemplateFile : " + + mConcurrencyCfgTemplateFile); + } + reader = new BufferedReader(new FileReader(mConcurrencyCfgTemplateFile)); + for (String key = reader.readLine(); key != null; + key = reader.readLine()) { + if (key != null) { + Log.d(TAG, "mConcurrencyCfgTemplateFile line: " + key); + } + if (key.startsWith(ENABLE_STA_SAP)) { + String st = key.replace(ENABLE_STA_SAP, ""); + st = st.replace(SEPARATOR_KEY, ""); + try { + mStaAndApConcurrency = Integer.parseInt(st); + Log.d(TAG,"mConcurrencyCfgTemplateFile EnableConcurrency = " + + mStaAndApConcurrency); + } catch (NumberFormatException e) { + Log.e(TAG,"mConcurrencyCfgTemplateFile: incorrect format :" + + key); + } + } + if (key.startsWith(SAP_INTERFACE_NAME)) { + String st = key.replace(SAP_INTERFACE_NAME, ""); + st = st.replace(SEPARATOR_KEY, ""); + try { + mSoftApInterfaceName = st; + Log.d(TAG,"mConcurrencyCfgTemplateFile SAPInterfaceName = " + + mSoftApInterfaceName); + } catch (NumberFormatException e) { + Log.e(TAG,"mConcurrencyCfgTemplateFile: incorrect format :" + + key); + } + } + if (key.startsWith(SAP_CHANNEL)) { + String st = key.replace(SAP_CHANNEL, ""); + st = st.replace(SEPARATOR_KEY, ""); + try { + mSoftApChannel = Integer.parseInt(st); + Log.d(TAG,"mConcurrencyCfgTemplateFile SAPChannel = " + + mSoftApChannel); + } catch (NumberFormatException e) { + Log.e(TAG,"mConcurrencyCfgTemplateFile: incorrect format :" + + key); + } + } + } + } catch (EOFException ignore) { + if (reader != null) { + try { + reader.close(); + reader = null; + } catch (Exception e) { + Log.e(TAG, "mConcurrencyCfgTemplateFile: Error closing file" + e); + } + } + } catch (IOException e) { + Log.e(TAG, "mConcurrencyCfgTemplateFile: Error parsing configuration" + e); + } + if (reader != null) { + try { + reader.close(); + } catch (Exception e) { + Log.e(TAG, "mConcurrencyCfgTemplateFile: Error closing file" + e); + } + } + } + + private boolean ensureConcurrencyFileExist() { + FileOutputStream dstStream = null; + FileInputStream srcStream = null; + DataInputStream in = null; + // check ConcurrencyCfgTemplateFile exist + try { + in = new DataInputStream(new BufferedInputStream(new FileInputStream( + mConcurrencyCfgTemplateFile))); + } catch (Exception e) { + Log.e(TAG, "ensureConcurrencyFile template file doesnt exist" + e); + return false; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) {} + } + } + return true; + } + + private void handleSubSystemRestart() { + mSubSystemRestart = true; + setWifiApEnabled(null, false); + } } diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index 89aabcf3e..f34d0b1e9 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -26,6 +26,7 @@ import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; +import static android.provider.Settings.Secure.WIFI_DISCONNECT_DELAY_DURATION; import android.Manifest; import android.app.ActivityManager; @@ -94,6 +95,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; +import android.os.SystemProperties; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -202,6 +204,12 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private final PropertyService mPropertyService; private final BuildProperties mBuildProperties; private final WifiCountryCode mCountryCode; + private boolean mStaAndAPConcurrency = false; + private SoftApStateMachine mSoftApStateMachine = null; + + + private int mNumSelectiveChannelScan = 0; + private int mMaxInitialSavedChannelScan; /* Scan results handling */ private List<ScanDetail> mScanResults = new ArrayList<>(); @@ -218,6 +226,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private final boolean mBackgroundScanSupported; private final String mInterfaceName; + /* The interface for ipManager */ + private String mDataInterfaceName; /* Tethering interface could be separate from wlan interface */ private String mTetherInterfaceName; @@ -263,6 +273,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private boolean testNetworkDisconnect = false; private boolean mEnableRssiPolling = false; + private boolean mIsRandomMacCleared = false; private int mRssiPollToken = 0; /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE * In CONNECT_MODE, the STA can scan and connect to an access point @@ -406,6 +417,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss autoRoamSetBSSID(mWifiConfigManager.getWifiConfiguration(netId), bssid); } + public int getScanCount() { + return mNumSelectiveChannelScan; + } + + public int getMaxConfiguredScanCount() { + return mMaxInitialSavedChannelScan; + } + public void setScanCount(int count) { + mNumSelectiveChannelScan = count; + } + public boolean autoRoamSetBSSID(WifiConfiguration config, String bssid) { boolean ret = true; if (mTargetRoamBSSID == null) mTargetRoamBSSID = "any"; @@ -527,7 +549,21 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } - private final IpManager mIpManager; + private IpManager mIpManager; + public SoftApStateMachine getSoftApStateMachine() { + return mSoftApStateMachine; + } + + public void setStaSoftApConcurrency() { + mStaAndAPConcurrency = true; + mSoftApStateMachine = + new SoftApStateMachine(mContext, this, mFacade, mInterfaceName, + mWifiConfigManager, mWifiMonitor, + mBackupManagerProxy, + mNwService, mBatteryStats, mCountryCode); + logd("mSoftApStateMachine is created"); + } + private AlarmManager mAlarmManager; private PendingIntent mScanIntent; @@ -568,6 +604,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // Provide packet filter capabilities to ConnectivityService. private final NetworkMisc mNetworkMisc = new NetworkMisc(); + private static final int WIFI_AUTO_CONNECT_TYPE_AUTO = 0; /* The base for wifi message types */ static final int BASE = Protocol.BASE_WIFI; @@ -875,6 +912,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private long mSupplicantScanIntervalMs; /** + * Delay configured for delayed disconnect. + **/ + private int mDisconnectDelayDuration; + + /** * Minimum time interval between enabling all networks. * A device can end up repeatedly connecting to a bad network on screen on/off toggle * due to enabling every time. We add a threshold to avoid this. @@ -1019,6 +1061,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // TODO refactor WifiNative use of context out into it's own class mWifiNative.initContext(mContext); mInterfaceName = mWifiNative.getInterfaceName(); + + updateDataInterface(); + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); mBatteryStats = IBatteryStats.Stub.asInterface(mFacade.getService( BatteryStats.SERVICE_NAME)); @@ -1058,7 +1103,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; mLastSignalLevel = -1; - mIpManager = mFacade.makeIpManager(mContext, mInterfaceName, new IpManagerCallback()); + mIpManager = mFacade.makeIpManager(mContext, mDataInterfaceName, new IpManagerCallback()); mIpManager.setMulticastFilter(true); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); @@ -1080,6 +1125,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mPrimaryDeviceType = mContext.getResources().getString( R.string.config_wifi_p2p_device_type); + mMaxInitialSavedChannelScan = mContext.getResources().getInteger( + R.integer.config_max_initial_scans_on_selective_channels); + mCountryCode = countryCode; mUserWantsSuspendOpt.set(mFacade.getIntegerSetting(mContext, @@ -1130,6 +1178,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss }, new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED)); + mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( + Settings.Global.WIFI_AUTO_CONNECT_TYPE), false, + new ContentObserver(getHandler()) { + @Override + public void onChange(boolean selfChange) { + checkAndSetAutoConnection(); + } + }); PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName()); @@ -1343,6 +1399,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (mWifiConnectivityManager != null) { mWifiConnectivityManager.enableVerboseLogging(mVerboseLoggingLevel); } + if (mStaAndAPConcurrency) { + mSoftApStateMachine.enableVerboseLogging(mVerboseLoggingLevel); + } } private static final String SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL = "log.tag.WifiHAL"; @@ -1399,6 +1458,32 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss return mWifiConfigManager.getEnableAutoJoinWhenAssociated(); } + private void updateDataInterface() { + String defaultRateUpgradeInterfaceName = "bond0"; // interface used for fst + int fstEnabled = SystemProperties.getInt("persist.fst.rate.upgrade.en", 0); + String prevDataInterfaceName = mDataInterfaceName; + String rateUpgradeDataInterfaceName = SystemProperties.get("persist.fst.data.interface", + defaultRateUpgradeInterfaceName); + + // When fst is not enabled, data interface is the same as the wlan interface + mDataInterfaceName = (fstEnabled == 1) ? rateUpgradeDataInterfaceName : mInterfaceName; + + // as long as we did not change from fst enabled to disabled state + // and vise-versa data interface does not change + if (mDataInterfaceName.equals(prevDataInterfaceName)) { + return; + } + + logd("fst " + ((fstEnabled == 1) ? "enabled" : "disabled")); + + if (mIpManager != null) { + mIpManager.shutdown(); + mIpManager = mFacade.makeIpManager(mContext, mDataInterfaceName, + new IpManagerCallback()); + mIpManager.setMulticastFilter(true); + } + } + private boolean setRandomMacOui() { String oui = mContext.getResources().getString(R.string.config_wifi_random_mac_oui); if (TextUtils.isEmpty(oui)) { @@ -1413,6 +1498,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss logd("Setting OUI to " + oui); return mWifiNative.setScanningMacOui(ouiBytes); } + private boolean clearRandomMacOui() { + byte[] ouiBytes = new byte[]{0,0,0}; + logd("Clear random OUI"); + return mWifiNative.setScanningMacOui(ouiBytes); + } /** * ****************************************************** @@ -1575,8 +1665,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } if (stats == null || mWifiLinkLayerStatsSupported <= 0) { - long mTxPkts = mFacade.getTxPackets(mInterfaceName); - long mRxPkts = mFacade.getRxPackets(mInterfaceName); + long mTxPkts = mFacade.getTxPackets(mDataInterfaceName); + long mRxPkts = mFacade.getRxPackets(mDataInterfaceName); mWifiInfo.updatePacketRates(mTxPkts, mRxPkts); } else { mWifiInfo.updatePacketRates(stats); @@ -1626,13 +1716,20 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } Set<Integer> freqs = null; - if (settings != null && settings.channelSet != null) { - freqs = new HashSet<Integer>(); - for (WifiChannel channel : settings.channelSet) { - freqs.add(channel.freqMHz); + freqs = new HashSet<Integer>(); + if (mNumSelectiveChannelScan < mMaxInitialSavedChannelScan) { + freqs = mWifiConfigManager.getConfiguredChannelList(); + } + if (freqs != null && (freqs.size() == 0)) { + freqs = null; + } + if (freqs == null) { + if (settings != null && settings.channelSet != null) { + for (WifiChannel channel : settings.channelSet) { + freqs.add(channel.freqMHz); + } } } - // Retrieve the list of hidden networkId's to scan for. Set<Integer> hiddenNetworkIds = mWifiConfigManager.getHiddenConfiguredNetworkIds(); @@ -1716,6 +1813,12 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() { // ignore all events since WifiStateMachine is registered for the supplicant events public void onSuccess() { + /* As part of optimizing time for initial scans for + * saved profiles, increment the scan trigger count + * upon receiving a success. + */ + if (mNumSelectiveChannelScan < mMaxInitialSavedChannelScan) + mNumSelectiveChannelScan++; } public void onFailure(int reason, String description) { mIsScanOngoing = false; @@ -1740,9 +1843,26 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss */ public void setSupplicantRunning(boolean enable) { if (enable) { + WifiNative.setApMode(false); sendMessage(CMD_START_SUPPLICANT); } else { - sendMessage(CMD_STOP_SUPPLICANT); + mDisconnectDelayDuration = -1; + try { + mDisconnectDelayDuration = Settings.Secure.getInt(mContext.getContentResolver(), + WIFI_DISCONNECT_DELAY_DURATION,0) ; + } catch (NumberFormatException ex) { + mDisconnectDelayDuration = 0; + Log.e(TAG, " get mDisconnectDelayDuration caught exception "); + } + if ((mDisconnectDelayDuration > 0) && (mNetworkInfo.getState() + == NetworkInfo.State.CONNECTED)) { + Intent intent = new Intent(WifiManager.ACTION_WIFI_DISCONNECT_IN_PROGRESS); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + Log.e(TAG, " Disconnection delayed by " + mDisconnectDelayDuration + " seconds"); + sendMessageDelayed(CMD_STOP_SUPPLICANT, mDisconnectDelayDuration * 1000); + } else { + sendMessage(CMD_STOP_SUPPLICANT); + } } } @@ -1751,6 +1871,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss */ public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) { if (enable) { + WifiNative.setApMode(true); sendMessage(CMD_START_AP, wifiConfig); } else { sendMessage(CMD_STOP_AP); @@ -1758,10 +1879,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } public void setWifiApConfiguration(WifiConfiguration config) { + if (mStaAndAPConcurrency) { + mSoftApStateMachine.setWifiApConfiguration(config); + return ; + } mWifiApConfigStore.setApConfiguration(config); } public WifiConfiguration syncGetWifiApConfiguration() { + if (mStaAndAPConcurrency) { + return mSoftApStateMachine.syncGetWifiApConfiguration(); + } return mWifiApConfigStore.getApConfiguration(); } @@ -1796,6 +1924,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss * TODO: doc */ public int syncGetWifiApState() { + if (mStaAndAPConcurrency) { + return mSoftApStateMachine.syncGetWifiApState(); + } return mWifiApState.get(); } @@ -2329,7 +2460,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (args.length > 1 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0]) + if (args != null && args.length > 1 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0]) && WifiMetrics.CLEAN_DUMP_ARG.equals(args[1])) { // Dump only wifi metrics serialized proto bytes (base64) updateWifiMetrics(); @@ -3594,7 +3725,16 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss stopRssiMonitoringOffload(); clearCurrentConfigBSSID("handleNetworkDisconnect"); - + if (mContext.getResources().getBoolean(R.bool.wifi_autocon) + && !shouldAutoConnect()) { + /* + * The following logic shall address the requirement for the DUT to + * not reconnect to the last connected network when the Auto + * Connect is disabled. This asks for the user prompt for any + * connection attempt (as per the requirement) + */ + disableLastNetwork(); + } stopIpManager(); /* Reset data structures */ @@ -3637,27 +3777,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } void handlePreDhcpSetup() { - if (!mBluetoothConnectionActive) { - /* - * There are problems setting the Wi-Fi driver's power - * mode to active when bluetooth coexistence mode is - * enabled or sense. - * <p> - * We set Wi-Fi to active mode when - * obtaining an IP address because we've found - * compatibility issues with some routers with low power - * mode. - * <p> - * In order for this active power mode to properly be set, - * we disable coexistence mode until we're done with - * obtaining an IP address. One exception is if we - * are currently connected to a headset, since disabling - * coexistence would interrupt that connection. - */ - // Disable the coexistence mode - mWifiNative.setBluetoothCoexistenceMode( - mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); - } + // Disable the coexistence mode + mWifiNative.setBluetoothCoexistenceMode( + mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); // Disable power save and suspend optimizations during DHCP // Note: The order here is important for now. Brcm driver changes @@ -3878,8 +4000,10 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } try { - mNwService.wifiFirmwareReload(mInterfaceName, "AP"); - if (DBG) Log.d(TAG, "Firmware reloaded in AP mode"); + if (!SystemProperties.getBoolean("ro.disableWifiApFirmwareReload", false)) { + mNwService.wifiFirmwareReload(mInterfaceName, "AP"); + if (DBG) Log.d(TAG, "Firmware reloaded in AP mode"); + } } catch (Exception e) { Log.e(TAG, "Failed to reload AP firmware " + e); } @@ -4322,8 +4446,19 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss class InitialState extends State { @Override public void enter() { - mWifiNative.stopHal(); - mWifiNative.unloadDriver(); + boolean skipUnload = false; + if (mStaAndAPConcurrency) { + int wifiApState = mSoftApStateMachine.syncGetWifiApState(); + if ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || + (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED)) { + log("Avoid unloading driver, AP_STATE is enabled/enabling"); + skipUnload = true; + } + } + if (!skipUnload) { + mWifiNative.stopHal(); + mWifiNative.unloadDriver(); + } if (mWifiP2pChannel == null) { mWifiP2pChannel = new AsyncChannel(); mWifiP2pChannel.connect(mContext, getHandler(), @@ -4340,6 +4475,12 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss logStateAndMessage(message, this); switch (message.what) { case CMD_START_SUPPLICANT: + /* Stop a running supplicant after a runtime restart + * Avoids issues with drivers that do not handle interface down + * on a running supplicant properly. + */ + mWifiMonitor.killSupplicant(mP2pSupported); + if (mWifiNative.loadDriver()) { try { mNwService.wifiFirmwareReload(mInterfaceName, "STA"); @@ -4356,33 +4497,29 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // Ensure interface is down and we have no IP // addresses before a supplicant start. mNwService.setInterfaceDown(mInterfaceName); - mNwService.clearInterfaceAddresses(mInterfaceName); + mNwService.clearInterfaceAddresses(mDataInterfaceName); // Set privacy extensions - mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); + mNwService.setInterfaceIpv6PrivacyExtensions(mDataInterfaceName, true); // IPv6 is enabled only as long as access point is connected since: // - IPv6 addresses and routes stick around after disconnection // - kernel is unaware when connected and fails to start IPv6 negotiation // - kernel can start autoconfiguration when 802.1x is not complete - mNwService.disableIpv6(mInterfaceName); + mNwService.disableIpv6(mDataInterfaceName); } catch (RemoteException re) { loge("Unable to change interface settings: " + re); } catch (IllegalStateException ie) { loge("Unable to change interface settings: " + ie); } - /* Stop a running supplicant after a runtime restart - * Avoids issues with drivers that do not handle interface down - * on a running supplicant properly. - */ - mWifiMonitor.killSupplicant(mP2pSupported); - if (mWifiNative.startHal() == false) { /* starting HAL is optional */ loge("Failed to start HAL"); } + updateDataInterface(); + if (mWifiNative.startSupplicant(mP2pSupported)) { setSupplicantLogLevel(); setWifiState(WIFI_STATE_ENABLING); @@ -4477,7 +4614,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (mWifiConfigManager.mEnableVerboseLogging.get() > 0) { enableVerboseLogging(mWifiConfigManager.mEnableVerboseLogging.get()); } + if (mContext.getResources().getBoolean(R.bool.wifi_autocon) + && !shouldAutoConnect()) { + mWifiConfigManager.disableAllNetworksNative(); + } initializeWpsDetails(); + mWifiNative.enableTdlsExtControl(); + mWifiNative.disableScanOffload(); + mWifiNative.setP2pDisable(); sendSupplicantConnectionChangedBroadcast(true); transitionTo(mDriverStartedState); @@ -4532,6 +4676,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss setRandomMacOui(); mWifiNative.enableAutoConnect(false); + checkAndSetAutoConnection(); mCountryCode.setReadyForChange(true); } @@ -4775,6 +4920,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiLogger.startLogging(DBG); mIsRunning = true; updateBatteryWorkSource(null); + mNumSelectiveChannelScan = 0; /** * Enable bluetooth coexistence scan mode when bluetooth connection is active. * When this mode is on, some of the low-level scan parameters used by the @@ -4898,7 +5044,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss break; case CMD_START_DRIVER: if (mOperationalMode == CONNECT_MODE) { - mWifiConfigManager.enableAllNetworks(); + if (mContext.getResources().getBoolean(R.bool.wifi_autocon) + && !shouldAutoConnect()) { + if (DBG) { + logd("Auto connect disabled, skip enable networks"); + } + } else { + mWifiConfigManager.enableAllNetworks(); + } } break; case CMD_SET_SUSPEND_OPT_ENABLED: @@ -5114,7 +5267,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiConfigManager.loadAndEnableAllNetworks(); mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P); } else { - mWifiConfigManager.enableAllNetworks(); + if (mContext.getResources().getBoolean(R.bool.wifi_autocon) + && !shouldAutoConnect()) { + if (DBG) { + logd("No auto, skip enable networks on mode change"); + } + } else { + mWifiConfigManager.enableAllNetworks(); + } } // Loose last selection choice since user toggled WiFi @@ -5463,6 +5623,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION); break; case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: + Intent intent = new Intent(WifiManager.ACTION_AUTH_PASSWORD_WRONG); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_AUTH_FAILURE); mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT); if (mTargetNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { @@ -5527,7 +5689,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // If we have COMPLETED a connection to a BSSID, start doing // DNAv4/DNAv6 -style probing for on-link neighbors of // interest (e.g. routers); harmless if none are configured. - if (state == SupplicantState.COMPLETED) { + if (isRoaming() && state == SupplicantState.COMPLETED) { mIpManager.confirmConfiguration(); } break; @@ -6151,7 +6313,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss log("Reconfiguring IP on connection"); // TODO: clear addresses and disable IPv6 // to simplify obtainingIpState. - transitionTo(mObtainingIpState); + mWifiNative.disconnect(); + handleNetworkDisconnect(); + transitionTo(mDisconnectedState); } if (result.hasProxyChanged()) { log("Reconfiguring proxy on connection"); @@ -6243,6 +6407,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss WpsResult wpsResult; switch (wpsInfo.setup) { case WpsInfo.PBC: + clearRandomMacOui(); + mIsRandomMacCleared = true; wpsResult = mWifiConfigManager.startWpsPbc(wpsInfo); break; case WpsInfo.KEYPAD: @@ -6339,6 +6505,25 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss res = mWifiConfigManager.matchProviderWithCurrentNetwork((String) message.obj); replyToMessage(message, message.what, res); break; + case WifiMonitor.SUBNET_STATUS_UPDATE_EVENT: + // subnet status change event comes from the supplicant + // after roaming. IP refresh is required if the device + // has roamed into a different IP subnet + if (DBG) log("SUBNET_STATUS_UPDATE_EVENT event"); + if (getNetworkDetailedState() == DetailedState.CONNECTED) { + int subnetStatus = message.arg1; + // 0 = unknown, 1 = unchanged, 2 = changed + if (subnetStatus == 2) { + if (DBG) log("Change in IP subnet, announce loss of IP reachability"); + sendMessage(CMD_IP_REACHABILITY_LOST); + } + } + break; + case CMD_IP_REACHABILITY_LOST: + if (DBG && message.obj != null) log((String) message.obj); + handleIpReachabilityLost(); + transitionTo(mDisconnectingState); + break; default: return NOT_HANDLED; } @@ -7166,8 +7351,20 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss testNetworkDisconnectCounter, 0), 15000); } - // Reenable all networks, allow for hidden networks to be scanned - mWifiConfigManager.enableAllNetworks(); + if (!getEnableAutoJoinWhenAssociated()) { + if (mContext.getResources().getBoolean(R.bool.wifi_autocon) + && !shouldAutoConnect()) { + if (DBG) { + logd("Auto connect disabled, skip enable networks"); + } + } else { + // Reenable all networks, allow for hidden networks to be scanned + mWifiConfigManager.enableAllNetworks(); + } + } else { + // Reenable all networks, allow for hidden networks to be scanned + mWifiConfigManager.enableAllNetworks(); + } mLastDriverRoamAttempt = 0; mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID; @@ -7421,6 +7618,25 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mNetworkAgent.onPacketKeepaliveEvent(slot, result); break; } + case WifiMonitor.SUBNET_STATUS_UPDATE_EVENT: + // subnet status change event comes from the supplicant + // after roaming. IP refresh is required if the device + // has roamed into a different IP subnet + if (DBG) log("SUBNET_STATUS_UPDATE_EVENT event"); + if (getNetworkDetailedState() == DetailedState.CONNECTED) { + int subnetStatus = message.arg1; + // 0 = unknown, 1 = unchanged, 2 = changed + if (subnetStatus == 2) { + if (DBG) log("Change in IP subnet, announce loss of IP reachability"); + sendMessage(CMD_IP_REACHABILITY_LOST); + } + } + break; + case CMD_IP_REACHABILITY_LOST: + if (DBG && message.obj != null) log((String) message.obj); + handleIpReachabilityLost(); + transitionTo(mDisconnectingState); + break; default: return NOT_HANDLED; } @@ -7765,6 +7981,10 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss public void exit() { mWifiConfigManager.enableAllNetworks(); mWifiConfigManager.loadConfiguredNetworks(); + if (mIsRandomMacCleared) { + setRandomMacOui(); + mIsRandomMacCleared = false; + } } } @@ -8341,6 +8561,42 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss return null; } + boolean shouldAutoConnect() { + int autoConnectPolicy = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.WIFI_AUTO_CONNECT_TYPE, + WIFI_AUTO_CONNECT_TYPE_AUTO); + if (DBG) { + if (autoConnectPolicy == WIFI_AUTO_CONNECT_TYPE_AUTO) { + Log.d(TAG, "Wlan connection type is auto, should auto connect"); + } else { + Log.d(TAG, "Shouldn't auto connect"); + } + } + return (autoConnectPolicy == WIFI_AUTO_CONNECT_TYPE_AUTO); + } + + void disableLastNetwork() { + if (getCurrentState() != mSupplicantStoppingState) { + mWifiConfigManager.disableNetwork(mLastNetworkId); + } + } + + void checkAndSetAutoConnection() { + if (mContext.getResources().getBoolean(R.bool.wifi_autocon)) { + if (shouldAutoConnect()){ + mWifiQualifiedNetworkSelector.skipQualifiedNetworkSelectionForAutoConnect(false); + } else { + mWifiQualifiedNetworkSelector.skipQualifiedNetworkSelectionForAutoConnect(true); + /* + * This is AutoConnect -> Manual selection case + * Device should not auto connect to network, hence + * disable supplicants auto connection ability. + */ + mWifiNative.enableAutoConnect(false); + } + } + } /** * Check if there is any connection request for WiFi network. * Note, caller of this helper function must acquire mWifiReqCountLock. diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java b/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java index e967212e7..ea94457bb 100644 --- a/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java +++ b/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java @@ -233,12 +233,16 @@ public class PasspointManagementObjectManager { if (!mEnabled) { throw new IOException("HS2.0 not enabled on this device"); } - if (mSPs.containsKey(homeSP.getFQDN())) { + if (mSPs.containsKey(homeSP.getFQDN()) + && getHomeSP(homeSP.getFQDN()).getCredential() != null + && getHomeSP(homeSP.getFQDN()).getCredential().getImsi() != null + && getHomeSP(homeSP.getFQDN()).getCredential().getImsi() + .equals(homeSP.getCredential().getImsi())) { Log.d(Utils.hs2LogTag(getClass()), "HS20 profile for " + homeSP.getFQDN() + " already exists"); return; } - Log.d(Utils.hs2LogTag(getClass()), "Adding new HS20 profile for " + homeSP.getFQDN()); + Log.d(Utils.hs2LogTag(getClass()), "Adding or updating HS20 profile for " + homeSP.getFQDN()); OMAConstructed dummyRoot = new OMAConstructed(null, TAG_PerProviderSubscription, null); buildHomeSPTree(homeSP, dummyRoot, mSPs.size() + 1); diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java index c1d9445b0..8f02b7824 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java @@ -186,6 +186,9 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { public static final int ENABLED = 1; public static final int DISABLED = 0; + static final int P2P_BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1; + static final int P2P_BLUETOOTH_COEXISTENCE_MODE_SENSE = 2; + private final boolean mP2pSupported; private WifiP2pDevice mThisDevice = new WifiP2pDevice(); @@ -198,6 +201,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { /* Invitation to join an existing p2p group */ private boolean mJoinExistingGroup; + private boolean mIsInvite = false; + /* Track whether we are in p2p discovery. This is used to avoid sending duplicate * broadcasts */ @@ -569,6 +574,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { }); private final WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo(); private WifiP2pGroup mGroup; + private boolean mIsBTCoexDisabled = false; + private boolean mPendingReformGroupIndication = false; // Saved WifiP2pConfig for an ongoing peer connection. This will never be null. // The deviceAddress will be an empty string when the device is inactive @@ -1303,6 +1310,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { @Override public void enter() { if (DBG) logd(getName()); + mIsInvite = false; mSavedPeerConfig.invalidate(); } @@ -1398,16 +1406,28 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mAutonomousGroup = false; mJoinExistingGroup = true; + mIsInvite = true; transitionTo(mUserAuthorizingInviteRequestState); break; case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: - case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: //We let the supplicant handle the provision discovery response //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT. //Handling provision discovery and issuing a p2p_connect before //group negotiation comes through causes issues break; + case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: + WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; + WifiP2pDevice device = provDisc.device; + if (device == null) { + Slog.d(TAG, "Device entry is null"); + break; + } + notifyP2pProvDiscShowPinRequest(provDisc.pin, device.deviceAddress); + mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); + sendPeersChangedBroadcast(); + transitionTo(mGroupNegotiationState); + break; case WifiP2pManager.CREATE_GROUP: mAutonomousGroup = true; int netId = message.arg1; @@ -1723,6 +1743,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { @Override public void enter() { if (DBG) logd(getName()); + mPendingReformGroupIndication = false; } @Override @@ -1735,6 +1756,13 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT: if (DBG) logd(getName() + " go success"); break; + // Action of removing and reforming group will be taken + // when we enter in GroupCreatedState + case WifiMonitor.P2P_REMOVE_AND_REFORM_GROUP_EVENT: + logd("P2P_REMOVE_AND_REFORM_GROUP_EVENT event received" + + " in GroupNegotiationState state"); + mPendingReformGroupIndication = true; + break; case WifiMonitor.P2P_GROUP_STARTED_EVENT: mGroup = (WifiP2pGroup) message.obj; if (DBG) logd(getName() + " group started"); @@ -1743,7 +1771,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { /* * update cache information and set network id to mGroup. */ - updatePersistentNetworks(NO_RELOAD); + updatePersistentNetworks(RELOAD); String devAddr = mGroup.getOwner().deviceAddress; mGroup.setNetworkId(mGroups.getNetworkId(devAddr, mGroup.getNetworkName())); @@ -1766,6 +1794,9 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } else { mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S); startIpManager(mGroup.getInterface()); + mWifiNative.setBluetoothCoexistenceMode( + P2P_BLUETOOTH_COEXISTENCE_MODE_DISABLED); + mIsBTCoexDisabled = true; WifiP2pDevice groupOwner = mGroup.getOwner(); WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress); if (peer != null) { @@ -1936,24 +1967,50 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } class GroupCreatedState extends State { + + private boolean handlP2pGroupRestart() { + boolean remove = true; + if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) { + Slog.d(TAG, "Removed P2P group successfully"); + transitionTo(mOngoingGroupRemovalState); + } else { + Slog.d(TAG, "Failed to remove the P2P group"); + handleGroupRemoved(); + transitionTo(mInactiveState); + remove = false; + } + if (mAutonomousGroup) { + Slog.d(TAG, "AutonomousGroup is set, reform P2P Group"); + sendMessage(WifiP2pManager.CREATE_GROUP); + } else { + Slog.d(TAG, "AutonomousGroup is not set, will not reform P2P Group"); + } + return remove; + } + @Override public void enter() { - if (DBG) logd(getName()); - // Once connected, peer config details are invalid - mSavedPeerConfig.invalidate(); - mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); + logd(getName() + "mPendingReformGroupIndication=" + mPendingReformGroupIndication); + if (mPendingReformGroupIndication) { + mPendingReformGroupIndication = false; + handlP2pGroupRestart(); + } else { + // Once connected, peer config details are invalid + mSavedPeerConfig.invalidate(); + mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); - updateThisDevice(WifiP2pDevice.CONNECTED); + updateThisDevice(WifiP2pDevice.CONNECTED); - //DHCP server has already been started if I am a group owner - if (mGroup.isGroupOwner()) { - setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS)); - } + //DHCP server has already been started if I am a group owner + if (mGroup.isGroupOwner()) { + setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS)); + } - // In case of a negotiation group, connection changed is sent - // after a client joins. For autonomous, send now - if (mAutonomousGroup) { - sendP2pConnectionChangedBroadcast(); + // In case of a negotiation group, connection changed is sent + // after a client joins. For autonomous, send now + if (mAutonomousGroup) { + sendP2pConnectionChangedBroadcast(); + } } } @@ -2014,6 +2071,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { break; case IPM_POST_DHCP_ACTION: mWifiNative.setP2pPowerSave(mGroup.getInterface(), true); + enableBTCoex(); break; case IPM_DHCP_RESULTS: mDhcpResults = (DhcpResults) message.obj; @@ -2036,6 +2094,11 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { break; case WifiP2pManager.REMOVE_GROUP: if (DBG) logd(getName() + " remove group"); + /* We need to check BTCOex state, because some times + * user can interupt connection before dhcp sucess, then + * BTcoex will be in disabled state. + */ + enableBTCoex(); if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) { transitionTo(mOngoingGroupRemovalState); replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED); @@ -2059,9 +2122,29 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { */ case WifiMonitor.P2P_GROUP_REMOVED_EVENT: if (DBG) logd(getName() + " group removed"); + /* We need to check BTCOex state, because if group + * is removed at GO side before dhcp sucess, then + * BTCoex will be in disabled state. + */ + enableBTCoex(); handleGroupRemoved(); + mWifiNative.p2pFlush(); transitionTo(mInactiveState); break; + case WifiMonitor.P2P_REMOVE_AND_REFORM_GROUP_EVENT: + /* First remove p2p group and then restart only if + * autonoums group formation is set to true + */ + Slog.d(TAG, "Received event P2P_REMOVE_AND_REFORM_GROUP, remove P2P group"); + if (handlP2pGroupRestart()) { + replyToMessage(message, + WifiP2pManager.REMOVE_GROUP_SUCCEEDED); + } else { + replyToMessage(message, + WifiP2pManager.REMOVE_GROUP_FAILED, + WifiP2pManager.ERROR); + } + break; case WifiMonitor.P2P_DEVICE_LOST_EVENT: device = (WifiP2pDevice) message.obj; //Device loss for a connected device indicates it is not in discovery any more @@ -2140,7 +2223,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mSavedPeerConfig.deviceAddress, false)) { // not found the client on the list loge("Already removed the client, ignore"); - break; } // try invitation. sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig); @@ -2407,6 +2489,36 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { dialog.show(); } + private void notifyP2pProvDiscShowPinRequest(String pin, String peerAddress) { + Resources r = Resources.getSystem(); + final String tempDevAddress = peerAddress; + final String tempPin = pin; + + final View textEntryView = LayoutInflater.from(mContext) + .inflate(R.layout.wifi_p2p_dialog, null); + + ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info); + addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress)); + addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title)) + .setView(textEntryView) + .setPositiveButton(r.getString(R.string.accept), new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mSavedPeerConfig = new WifiP2pConfig(); + mSavedPeerConfig.deviceAddress = tempDevAddress; + mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; + mSavedPeerConfig.wps.pin = tempPin; + mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); + } + }) + .setCancelable(false) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + private void notifyInvitationReceived() { Resources r = Resources.getSystem(); final WpsInfo wps = mSavedPeerConfig.wps; @@ -2590,15 +2702,22 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { * @param config for the peer */ private void p2pConnectWithPinDisplay(WifiP2pConfig config) { + boolean join = false; WifiP2pDevice dev = fetchCurrentDeviceDetails(config); + if (mIsInvite) { + join = true; + } else { + join = dev.isGroupOwner(); + } - String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner()); + String pin = mWifiNative.p2pConnect(config, join); try { Integer.parseInt(pin); notifyInvitationSent(pin, config.deviceAddress); } catch (NumberFormatException ignore) { // do nothing if p2pConnect did not return a pin } + mIsInvite = false; } /** @@ -2773,6 +2892,11 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { String deviceName = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.WIFI_P2P_DEVICE_NAME); if (deviceName == null) { + String wifi_direct_name = mContext.getResources().getString( + R.string.def_wifi_direct_name); + if (!TextUtils.isEmpty(wifi_direct_name)){ + return wifi_direct_name; + } /* We use the 4 digits of the ANDROID_ID to have a friendly * default that has low likelihood of collision with a peer */ String id = Settings.Secure.getString(mContext.getContentResolver(), @@ -3207,6 +3331,17 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return clientInfo; } + /** + * Enable BTCOEXMODE after DHCP or GROUP REMOVE + */ + private void enableBTCoex() { + if (mIsBTCoexDisabled) { + mWifiNative.setBluetoothCoexistenceMode( + P2P_BLUETOOTH_COEXISTENCE_MODE_SENSE); + mIsBTCoexDisabled = false; + } + } + } /** diff --git a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java index f0cac0fb6..ac5db5a4f 100644 --- a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; +import android.provider.Settings; import com.android.internal.R; import com.android.server.wifi.Clock; @@ -679,6 +680,14 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle Log.e(TAG, "Set priority failed for: " + network.networkId); return false; } + int autoConnectPolicy = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.WIFI_AUTO_CONNECT_TYPE,0); + if (autoConnectPolicy == 1) { + Log.d(TAG,"Do not enable network,since auto connect disabled"); + return true; + } + if (!mWifiNative.enableNetworkWithoutConnect(network.networkId)) { Log.e(TAG, "Enable network failed for: " + network.networkId); return false; diff --git a/service/jni/COPYING b/service/jni/COPYING new file mode 100755 index 000000000..7efce0dee --- /dev/null +++ b/service/jni/COPYING @@ -0,0 +1,22 @@ +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + + +See the README file for the current license terms. + +This software was previously distributed under BSD/GPL v2 dual license +terms that allowed either of those license alternatives to be +selected. As of February 11, 2012, the project has chosen to use only +the BSD license option for future distribution. As such, the GPL v2 +license option is no longer used. It should be noted that the BSD +license option (the one with advertisement clause removed) is compatible +with GPL and as such, does not prevent use of this software in projects +that use GPL. + +Some of the files may still include pointers to GPL version 2 license +terms. However, such copyright and license notifications are maintained +only for attribution purposes and any distribution of this software +after February 11, 2012 is no longer under the GPL v2 option. diff --git a/service/jni/README b/service/jni/README new file mode 100644 index 000000000..8de14a64f --- /dev/null +++ b/service/jni/README @@ -0,0 +1,56 @@ +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +These programs are licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. + + +This package may include either wpa_supplicant, hostapd, or both. See +README file respective subdirectories (wpa_supplicant/README or +hostapd/README) for more details. + +Source code files were moved around in v0.6.x releases and compared to +earlier releases, the programs are now built by first going to a +subdirectory (wpa_supplicant or hostapd) and creating build +configuration (.config) and running 'make' there (for Linux/BSD/cygwin +builds). + + +License +------- + +This software may be distributed, used, and modified under the terms of +BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/service/jni/com_android_server_wifi_Gbk2Utf.cpp b/service/jni/com_android_server_wifi_Gbk2Utf.cpp new file mode 100644 index 000000000..4b8c9b977 --- /dev/null +++ b/service/jni/com_android_server_wifi_Gbk2Utf.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#define LOG_TAG "wifi_gbk2utf" + +#include "jni.h" +#include "com_android_server_wifi_Gbk2Utf.h" + +#define BUF_SIZE 256 +#define CONVERT_LINE_LEN 2048 +#define CHARSET_CN ("gbk") + +namespace android { + +static jint DBG = false; + +struct accessPointObjectItem *g_pItemList = NULL; +struct accessPointObjectItem *g_pLastNode = NULL; +pthread_mutex_t *g_pItemListMutex = NULL; + +static void addAPObjectItem(const char *ssid, const char *ssid_utf8) +{ + if (NULL == ssid || NULL == ssid_utf8) { + ALOGE("ssid or ssid_utf8 is NULL"); + return; + } + + struct accessPointObjectItem *pTmpItemNode = NULL; + struct accessPointObjectItem *pItemNode = NULL; + bool foundItem = false; + + pthread_mutex_lock(g_pItemListMutex); + pTmpItemNode = g_pItemList; + while (pTmpItemNode) { + if (pTmpItemNode->ssid && (*(pTmpItemNode->ssid) == ssid)) { + foundItem = true; + break; + } + pTmpItemNode = pTmpItemNode->pNext; + } + if (foundItem) { + if (DBG) + ALOGD("Found AP %s", pTmpItemNode->ssid->string()); + } else { + pItemNode = new struct accessPointObjectItem(); + if (NULL == pItemNode) { + ALOGE("Failed to allocate memory for new item!"); + goto EXIT; + } + memset(pItemNode, 0, sizeof(accessPointObjectItem)); + pItemNode->ssid_utf8 = new String8(ssid_utf8); + if (NULL == pItemNode->ssid_utf8) { + ALOGE("Failed to allocate memory for new ssid_utf8!"); + delete pItemNode; + goto EXIT; + } + pItemNode->ssid = new String8(ssid); + if (NULL == pItemNode->ssid) { + ALOGE("Failed to allocate memory for new ssid!"); + delete pItemNode; + goto EXIT; + } + + pItemNode->pNext = NULL; + if (DBG) + ALOGD("AP doesn't exist, new one for %s", ssid); + if (NULL == g_pItemList) { + g_pItemList = pItemNode; + g_pLastNode = g_pItemList; + } else { + g_pLastNode->pNext = pItemNode; + g_pLastNode = pItemNode; + } + } + +EXIT: + pthread_mutex_unlock(g_pItemListMutex); +} + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + + +static int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +/* parse SSID string encoded from wpa_supplicant to normal string */ +static size_t ssid_decode(char *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len == maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + + return len; +} + +/* This function can be used to convert SSIDs into printable form. Since wifi + * framework layer needs to parse printable form string. +*/ +static void ssid_encode(char *txt, size_t maxlen, const char *data, unsigned int len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] < 127) { + *txt++ = data[i]; + } else { + txt += snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + *txt = '\0'; +} + +/* check if the SSID string is UTF coded */ +static bool isUTF8String(const char* str, int length) +{ + unsigned int nBytes = 0; + unsigned char chr; + bool bAllAscii = true; + for (int i = 0; i < length; i++) { + chr = *(str+i); + if ((chr & 0x80) != 0) { + bAllAscii = false; + } + if (0 == nBytes) { + if (chr >= 0x80) { + if (chr >= 0xFC && chr <= 0xFD) { + nBytes = 6; + } else if (chr >= 0xF8) { + nBytes = 5; + } else if (chr >= 0xF0) { + nBytes = 4; + } else if (chr >= 0xE0) { + nBytes = 3; + } else if (chr >= 0xC0) { + nBytes = 2; + } else { + return false; + } + nBytes--; + } + } else { + if ((chr & 0xC0) != 0x80) { + return false; + } + nBytes--; + } + } + + if (nBytes > 0 || bAllAscii) { + return false; + } + return true; +} + +/* + * https://en.wikipedia.org/wiki/GBK + * + * GBK character is encoded as 1 or 2 bytes. + * - A single byte with range 0x00-0x7f is ASCII. + * - A byte with the high bit set indicates that it is + * the first of 2 bytes. + * byte1: (0x81-0xFE) + * byte2: (0x40-0xFE) except 0x7F + * + * This function only returns true only it is GBK string + * but not all character is ASCII. + */ +static bool isGBKString(const char *str, int length) { + unsigned char byte1; + unsigned char byte2; + bool isAllASCII = true; + + for (int i = 0; i < length; i ++) { + byte1 = *(str+i); + + if (byte1 >= 0x81 && byte1 < 0xFF && (i+1) < length) { + byte2 = *(str+i+1); + if (byte2 >= 0x40 && byte2 < 0xFF && byte2 != 0x7F) { + // GBK + isAllASCII = false; + i ++; + continue; + } else { + return false; + } + } else if (byte1 < 0x80){ + // ASCII + continue; + } else { + return false; + } + } + + if (isAllASCII) + return false; + + return true; +} + +static void createFromHex(char *buf, int maxlen, const char *str) +{ + const char *pos = str; + int len = 0; + int val; + + while(*pos){ + if (len == maxlen) + break; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + } else { + buf[len++] = val; + pos += 2; + } + } +} + +static size_t createToHex(char *buf, size_t buf_size, const char *str, unsigned int len) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + if (buf_size == 0) + return 0; + for (i = 0; i < len; i++) { + ret = snprintf(pos, end - pos, "%02x", str[i]); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + end[-1] = '\0'; + return pos - buf; +} + +void parseScanResults(String16& str, const char *reply) +{ + unsigned int lineBeg = 0, lineEnd = 0; + size_t replyLen = strlen(reply); + char ssid[BUF_SIZE] = {0}; + char ssid_utf8[BUF_SIZE] = {0}; + char ssid_txt[BUF_SIZE] = {0}; + bool isUTF8 = false, isCh = false; + char buf[BUF_SIZE] = {0}; + String8 line; + + UConverterType conType = UCNV_UTF8; + char dest[CONVERT_LINE_LEN] = {0}; + UErrorCode err = U_ZERO_ERROR; + UConverter* pConverter = ucnv_open(CHARSET_CN, &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_open error"); + return; + } + + /* Parse every line of the reply to construct accessPointObjectItem list */ + for (lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) { + if (lineEnd == replyLen || '\n' == reply[lineEnd]) { + line.setTo(reply + lineBeg, lineEnd - lineBeg + 1); + if (DBG) + ALOGD("%s, line=%s ", __FUNCTION__, line.string()); + if (strncmp(line.string(), "ssid=", 5) == 0) { + sscanf(line.string() + 5, "%[^\n]", ssid); + ssid_decode(buf,BUF_SIZE,ssid); + isUTF8 = isUTF8String(buf,sizeof(buf)); + isCh = isGBKString(buf, sizeof(buf)); + if (DBG) + ALOGD("%s, ssid = %s, buf = %s,isUTF8= %d, isCh = %d", + __FUNCTION__, ssid, buf ,isUTF8, isCh); + if (!isUTF8 && isCh) { + ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, + buf, strlen(buf), &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_toUChars error"); + goto EXIT; + } + ssid_encode(ssid_txt, BUF_SIZE, dest, strlen(dest)); + if (DBG) + ALOGD("%s, ssid_txt = %s", __FUNCTION__,ssid_txt); + str += String16("ssid="); + str += String16(ssid_txt); + str += String16("\n"); + memset(ssid_utf8, 0, BUF_SIZE); + strlcpy(ssid_utf8, dest, BUF_SIZE); + memset(dest, 0, CONVERT_LINE_LEN); + memset(ssid_txt, 0, BUF_SIZE); + } else { + memset(buf, 0, BUF_SIZE); + str += String16(line.string()); + } + } else if (strncmp(line.string(), "====", 4) == 0) { + if (DBG) + ALOGD("After sscanf,ssid:%s, isCh:%d", + ssid, isCh); + if( !isUTF8 && isCh){ + addAPObjectItem(buf, ssid_utf8); + memset(buf, 0, BUF_SIZE); + } + } + if (strncmp(line.string(), "ssid=", 5) != 0) + str += String16(line.string()); + lineBeg = lineEnd + 1; + } + } + +EXIT: + ucnv_close(pConverter); +} + +void constructSsid(String16& str, const char *reply) +{ + char ssid[BUF_SIZE] = {0}; + char buf[BUF_SIZE] = {0}; + char ssid_txt[BUF_SIZE] ={0}; + bool isUTF8 = false, isCh = false; + + char dest[CONVERT_LINE_LEN] = {0}; + UConverterType conType = UCNV_UTF8; + UErrorCode err = U_ZERO_ERROR; + UConverter* pConverter = ucnv_open(CHARSET_CN, &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_open error"); + return; + } + sscanf(reply, "%[^\n]", ssid); + if (DBG) + ALOGD("%s, ssid = %s", __FUNCTION__, ssid); + createFromHex(buf, BUF_SIZE, ssid); + isUTF8 = isUTF8String(buf, sizeof(buf)); + isCh = isGBKString(buf, sizeof(buf)); + if (!isUTF8 && isCh) { + ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, + buf, strlen(buf), &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_toUChars error"); + goto EXIT; + } + createToHex(ssid_txt, strlen(dest)*2 + 1, dest, strlen(dest)); + if (DBG) + ALOGD("%s, ssid_txt = %s, dest = %s \n" , + __FUNCTION__, ssid_txt, dest); + str += String16(ssid_txt); + str += String16("\n"); + memset(dest, 0, CONVERT_LINE_LEN); + memset(buf, 0, BUF_SIZE); + memset(ssid_txt, 0, BUF_SIZE); + } else { + memset(buf, 0, BUF_SIZE); + str += String16(reply); + } + +EXIT: + ucnv_close(pConverter); +} + +jboolean setNetworkVariable(char *buf) +{ + struct accessPointObjectItem *pTmpItemNode = NULL; + bool gbk_found = false; + + unsigned int netId; + char name[BUF_SIZE] = {0}; + char value[BUF_SIZE] = {0}; + char interface[BUF_SIZE] = {0}; + char dummy[BUF_SIZE] = {0}; + char ssid[BUF_SIZE] = {0}; + if (strnlen(buf, BUF_SIZE) == BUF_SIZE) { + ALOGE("setNetworkVariable failed due to invalid length"); + return JNI_FALSE; + } + + /* parse SET_NETWORK command*/ + sscanf(buf, "%s %s %d %s %s", interface, dummy, &netId, name, value); + + /* L Framework will convert string to HEX, so we convert it back here for comparation */ + if (0 == strncmp(name, "ssid", 4)) { + createFromHex(ssid, BUF_SIZE, value); + } + + if (DBG) + ALOGD("parse SET_NETWORK command success, netId = %d, name = %s, value =%s, length=%d", + netId, name, value, strlen(value)); + + if (NULL == g_pItemListMutex) { + /* Driver is unloaded, g_pItemList, g_pItemListMutex are NULL */ + return JNI_TRUE; + } + pthread_mutex_lock(g_pItemListMutex); + pTmpItemNode = g_pItemList; + if (NULL == pTmpItemNode) { + if (DBG) + ALOGD("g_pItemList is NULL"); + } + while (pTmpItemNode) { + if (pTmpItemNode->ssid_utf8) { + ALOGD("ssid_utf8 = %s, length=%d, value =%s, length=%d", + pTmpItemNode->ssid_utf8->string(),strlen(pTmpItemNode->ssid_utf8->string()), ssid, strlen(ssid)); + + if (0 == strcmp(pTmpItemNode->ssid_utf8->string(), ssid)) { + gbk_found = true; + break; + } + } + pTmpItemNode = pTmpItemNode->pNext; + } + + if (0 == strncmp(name, "ssid", 4) && gbk_found) { + snprintf(buf, BUF_SIZE, "%s SET_NETWORK %d ssid \"%s\"", interface, netId, pTmpItemNode->ssid->string()); + if (DBG) + ALOGD("new SET_NETWORK command is: %s", buf); + } + + pthread_mutex_unlock(g_pItemListMutex); + + + return JNI_TRUE; +} + +void constructEventSsid(char *eventstr) +{ + char *tmp = NULL; + char ssid[BUF_SIZE] = {0}; + char ssid_txt[BUF_SIZE] = {0}; + char buf[BUF_SIZE] = {0}; + bool isUTF8 = false, isCh = false; + + UConverterType conType = UCNV_UTF8; + char dest[CONVERT_LINE_LEN] = {0}; + UErrorCode err = U_ZERO_ERROR; + UConverter* pConverter = ucnv_open(CHARSET_CN, &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_open error"); + return; + } + + tmp = strstr(eventstr, " SSID"); + if (tmp&&(strlen(tmp) > 6 )) { + if(!strstr(tmp,"=")) + sscanf(tmp + 7, "%[^\']", ssid); + else + sscanf(tmp + 6, "%s", ssid); + if (DBG) + ALOGD("%s, SSID = %s", __FUNCTION__, ssid); + ssid_decode(buf,BUF_SIZE,ssid); + isUTF8 = isUTF8String(buf,sizeof(buf)); + isCh = isGBKString(buf, sizeof(buf)); + if (!isUTF8 && isCh) { + ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, + buf, strlen(buf), &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_toUChars error"); + goto EXIT; + } + ssid_encode(ssid_txt, BUF_SIZE, dest, strlen(dest)); + if (!strstr(tmp,"=")) + snprintf(eventstr + (strlen(eventstr) - strlen(tmp)), strlen(eventstr), " SSID \'%s\'", ssid_txt); + else + snprintf(eventstr + (strlen(eventstr) - strlen(tmp)), strlen(eventstr), " SSID=%s", ssid_txt); + if (DBG) + ALOGD("%s, ssid_txt = %s, eventsrt = %s", __FUNCTION__, ssid_txt, eventstr); + } + } + +EXIT: + ucnv_close(pConverter); +} + +} //namespace android diff --git a/service/jni/com_android_server_wifi_Gbk2Utf.h b/service/jni/com_android_server_wifi_Gbk2Utf.h new file mode 100644 index 000000000..37e7a2b75 --- /dev/null +++ b/service/jni/com_android_server_wifi_Gbk2Utf.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <utils/String8.h> +#include <utils/String16.h> +#include <pthread.h> +#include <unicode/ucnv.h> +#include <unicode/ucsdet.h> +#include <net/if.h> +#include <sys/socket.h> +#include <linux/wireless.h> +#include <utils/misc.h> +#include <utils/Log.h> + +namespace android { + +struct accessPointObjectItem { + String8 *ssid_utf8; + String8 *ssid; + struct accessPointObjectItem *pNext; +}; + +extern void parseScanResults(String16& str, const char *reply); + +extern void constructSsid(String16& str, const char *reply); + +extern void constructEventSsid(char *eventstr); + +extern jboolean setNetworkVariable(char *buf); + +} //namespace android diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp index ffd5b559f..2dd4c03ba 100644 --- a/service/jni/com_android_server_wifi_WifiNative.cpp +++ b/service/jni/com_android_server_wifi_WifiNative.cpp @@ -20,9 +20,10 @@ #include "JniConstants.h" #include <ScopedUtfChars.h> #include <ScopedBytes.h> -#include <utils/misc.h> -#include <utils/Log.h> -#include <utils/String16.h> +//#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +//#include <utils/Log.h> +//#include <utils/String16.h> #include <ctype.h> #include <stdlib.h> #include <sys/socket.h> @@ -43,6 +44,11 @@ #define EVENT_BUF_SIZE 2048 #define WAKE_REASON_TYPE_MAX 10 +#define CONVERT_LINE_LEN 2048 +#define CHARSET_CN ("gbk") + +#include "com_android_server_wifi_Gbk2Utf.h" + namespace android { extern "C" @@ -53,13 +59,21 @@ constexpr int SAFE_NET_LOG_ID = 0x534e4554; //Please put all HAL function call here and call from the function table instead of directly call wifi_hal_fn hal_fn; +extern struct accessPointObjectItem *g_pItemList; +extern struct accessPointObjectItem *g_pLastNode; +extern pthread_mutex_t *g_pItemListMutex; +extern String8 *g_pCurrentSSID; static bool doCommand(JNIEnv* env, jstring javaCommand, char* reply, size_t reply_len) { ScopedUtfChars command(env, javaCommand); if (command.c_str() == NULL) { return false; // ScopedUtfChars already threw on error. } - + if(strstr(command.c_str(), "SET_NETWORK")) { + if(!setNetworkVariable((char *)command.c_str())) { + return false; + } + } if (DBG) { ALOGD("doCommand: %s", command.c_str()); } @@ -102,10 +116,30 @@ static jboolean doBooleanCommand(JNIEnv* env, jstring javaCommand) { // Send a command to the supplicant, and return the reply as a String. static jstring doStringCommand(JNIEnv* env, jstring javaCommand) { char reply[REPLY_BUF_SIZE]; + ScopedUtfChars command(env, javaCommand); + if (command.c_str() == NULL) { + return NULL; + } if (!doCommand(env, javaCommand, reply, sizeof(reply))) { return NULL; } - return env->NewStringUTF(reply); + if (DBG) ALOGD("cmd = %s, reply: %s", command.c_str(), reply); + String16 str; + if (strstr(command.c_str(),"BSS RANGE=")) { + parseScanResults(str,reply); + } else if (strstr(command.c_str(),"GET_NETWORK") && + strstr(command.c_str(),"ssid") && !strstr(command.c_str(),"bssid") + && !strstr(command.c_str(),"scan_ssid")){ + constructSsid(str, reply); + } else { + str += String16((char *)reply); + } + return env->NewString((const jchar *)str.string(), str.size()); +} + +static jboolean android_net_wifi_setApMode(JNIEnv* env, jobject, jboolean enable) +{ + return (jboolean)(::wifi_set_mode(enable ? 1 : 0) == 0); } static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jclass) @@ -115,11 +149,41 @@ static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jclass) static jboolean android_net_wifi_loadDriver(JNIEnv* env, jclass) { + g_pItemListMutex = new pthread_mutex_t; + if (NULL == g_pItemListMutex) { + ALOGE("Failed to allocate memory for g_pItemListMutex!"); + return JNI_FALSE; + } + pthread_mutex_init(g_pItemListMutex, NULL); return (::wifi_load_driver() == 0); } static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jclass) { + if (g_pItemListMutex != NULL) { + pthread_mutex_lock(g_pItemListMutex); + struct accessPointObjectItem *pCurrentNode = g_pItemList; + struct accessPointObjectItem *pNextNode = NULL; + while (pCurrentNode) { + pNextNode = pCurrentNode->pNext; + if (NULL != pCurrentNode->ssid) { + delete pCurrentNode->ssid; + pCurrentNode->ssid = NULL; + } + if (NULL != pCurrentNode->ssid_utf8) { + delete pCurrentNode->ssid_utf8; + pCurrentNode->ssid_utf8 = NULL; + } + delete pCurrentNode; + pCurrentNode = pNextNode; + } + g_pItemList = NULL; + g_pLastNode = NULL; + pthread_mutex_unlock(g_pItemListMutex); + pthread_mutex_destroy(g_pItemListMutex); + delete g_pItemListMutex; + g_pItemListMutex = NULL; + } return (::wifi_unload_driver() == 0); } @@ -148,6 +212,9 @@ static jstring android_net_wifi_waitForEvent(JNIEnv* env, jclass) char buf[EVENT_BUF_SIZE]; int nread = ::wifi_wait_for_event(buf, sizeof buf); if (nread > 0) { + if (strstr(buf, " SSID=") || strstr(buf, " SSID ")){ + constructEventSsid(buf); + } return env->NewStringUTF(buf); } else { return NULL; @@ -2540,6 +2607,7 @@ static JNINativeMethod gWifiMethods[] = { { "doIntCommandNative", "(Ljava/lang/String;)I", (void*)android_net_wifi_doIntCommand }, { "doStringCommandNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*) android_net_wifi_doStringCommand }, + { "setApMode", "(Z)Z", (void*) android_net_wifi_setApMode}, { "startHalNative", "()Z", (void*) android_net_wifi_startHal }, { "stopHalNative", "()V", (void*) android_net_wifi_stopHal }, { "waitForHalEventNative", "()V", (void*) android_net_wifi_waitForHalEvents }, |