Loading wifi/java/src/android/net/wifi/nl80211/InstantWifi.java 0 → 100644 +318 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 android.net.wifi.nl80211; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; /** * @hide */ public class InstantWifi { private static final String INSTANT_WIFI_TAG = "InstantWifi"; private static final int OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS = 1000; private static final int WIFI_NETWORK_EXPIRED_MS = 7 * 24 * 60 * 60 * 1000; // a week private static final String NO_CONNECTION_TIMEOUT_ALARM_TAG = INSTANT_WIFI_TAG + " No Connection Timeout"; private Context mContext; private AlarmManager mAlarmManager; private Handler mEventHandler; private ConnectivityManager mConnectivityManager; private WifiManager mWifiManager; private PowerManager mPowerManager; private long mLastWifiOnSinceBootMs; private long mLastScreenOnSinceBootMs; private boolean mIsWifiConnected = false; private boolean mScreenOn = false; private boolean mWifiEnabled = false; private boolean mIsNoConnectionAlarmSet = false; private ArrayList<WifiNetwork> mConnectedWifiNetworkList = new ArrayList<>(); private AlarmManager.OnAlarmListener mNoConnectionTimeoutCallback = new AlarmManager.OnAlarmListener() { public void onAlarm() { Log.i(INSTANT_WIFI_TAG, "Timed out waiting for wifi connection"); mIsNoConnectionAlarmSet = false; mWifiManager.startScan(); } }; public InstantWifi(Context context, AlarmManager alarmManager, Handler eventHandler) { mContext = context; mAlarmManager = alarmManager; mEventHandler = eventHandler; mWifiManager = mContext.getSystemService(WifiManager.class); mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mConnectivityManager.registerNetworkCallback( new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build(), new WifiNetworkCallback()); // System power service was initialized before wifi nl80211 service. mPowerManager = mContext.getSystemService(PowerManager.class); IntentFilter screenEventfilter = new IntentFilter(); screenEventfilter.addAction(Intent.ACTION_SCREEN_ON); screenEventfilter.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_ON)) { if (!mScreenOn) { mLastScreenOnSinceBootMs = getMockableElapsedRealtime(); } mScreenOn = true; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; } Log.d(INSTANT_WIFI_TAG, "mScreenOn is changed to " + mScreenOn); } }, screenEventfilter, null, mEventHandler); mScreenOn = mPowerManager.isInteractive(); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); mWifiEnabled = state == WifiManager.WIFI_STATE_ENABLED; if (mWifiEnabled) { mLastWifiOnSinceBootMs = getMockableElapsedRealtime(); } Log.d(INSTANT_WIFI_TAG, "mWifiEnabled is changed to " + mWifiEnabled); } }, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION), null, mEventHandler); } @VisibleForTesting protected long getMockableElapsedRealtime() { return SystemClock.elapsedRealtime(); } private class WifiNetwork { private final int mNetId; private Set<Integer> mConnectedFrequencies = new HashSet<Integer>(); private int[] mLastTwoConnectedFrequencies = new int[2]; private long mLastConnectedTimeMillis; WifiNetwork(int netId) { mNetId = netId; } public int getNetId() { return mNetId; } public boolean addConnectedFrequency(int channelFrequency) { mLastConnectedTimeMillis = getMockableElapsedRealtime(); if (mLastTwoConnectedFrequencies[0] != channelFrequency && mLastTwoConnectedFrequencies[1] != channelFrequency) { mLastTwoConnectedFrequencies[0] = mLastTwoConnectedFrequencies[1]; mLastTwoConnectedFrequencies[1] = channelFrequency; } return mConnectedFrequencies.add(channelFrequency); } public Set<Integer> getConnectedFrequencies() { return mConnectedFrequencies; } public int[] getLastTwoConnectedFrequencies() { if ((getMockableElapsedRealtime() - mLastConnectedTimeMillis) > WIFI_NETWORK_EXPIRED_MS) { return new int[0]; } return mLastTwoConnectedFrequencies; } public long getLastConnectedTimeMillis() { return mLastConnectedTimeMillis; } } private class WifiNetworkCallback extends NetworkCallback { @Override public void onAvailable(@NonNull Network network) { } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (networkCapabilities != null && network != null) { WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo(); if (wifiInfo == null || mWifiManager == null) { return; } WifiConfiguration config = mWifiManager.getPrivilegedConnectedNetwork(); if (config == null) { return; } final int currentNetworkId = config.networkId; final int connectecFrequency = wifiInfo.getFrequency(); if (connectecFrequency < 0 || currentNetworkId < 0) { return; } mIsWifiConnected = true; if (mIsNoConnectionAlarmSet) { mAlarmManager.cancel(mNoConnectionTimeoutCallback); } Log.d(INSTANT_WIFI_TAG, "Receive Wifi is connected, freq = " + connectecFrequency + " and currentNetworkId : " + currentNetworkId + ", wifiinfo = " + wifiInfo); boolean isExist = false; for (WifiNetwork wifiNetwork : mConnectedWifiNetworkList) { if (wifiNetwork.getNetId() == currentNetworkId) { if (wifiNetwork.addConnectedFrequency(connectecFrequency)) { Log.d(INSTANT_WIFI_TAG, "Update connected frequency: " + connectecFrequency + " to Network currentNetworkId : " + currentNetworkId); } isExist = true; } } if (!isExist) { WifiNetwork currentNetwork = new WifiNetwork(currentNetworkId); currentNetwork.addConnectedFrequency(connectecFrequency); if (mConnectedWifiNetworkList.size() < 5) { mConnectedWifiNetworkList.add(currentNetwork); } else { ArrayList<WifiNetwork> lastConnectedWifiNetworkList = new ArrayList<>(); WifiNetwork legacyNetwork = mConnectedWifiNetworkList.get(0); for (WifiNetwork connectedNetwork : mConnectedWifiNetworkList) { if (connectedNetwork.getNetId() == legacyNetwork.getNetId()) { continue; } // Keep the used recently network in the last connected list if (connectedNetwork.getLastConnectedTimeMillis() > legacyNetwork.getLastConnectedTimeMillis()) { lastConnectedWifiNetworkList.add(connectedNetwork); } else { lastConnectedWifiNetworkList.add(legacyNetwork); legacyNetwork = connectedNetwork; } } mConnectedWifiNetworkList = lastConnectedWifiNetworkList; } } } } @Override public void onLost(@NonNull Network network) { mIsWifiConnected = false; } } /** * Returns whether or not the scan freqs should be overrided by using predicted channels. */ public boolean isUsePredictedScanningChannels() { if (mIsWifiConnected || mConnectedWifiNetworkList.size() == 0 || !mWifiManager.isWifiEnabled() || !mPowerManager.isInteractive()) { return false; } if (!mWifiEnabled || !mScreenOn) { Log.d(INSTANT_WIFI_TAG, "WiFi/Screen State mis-match, run instant Wifi anyway!"); return true; } return (((getMockableElapsedRealtime() - mLastWifiOnSinceBootMs) < OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS) || ((getMockableElapsedRealtime() - mLastScreenOnSinceBootMs) < OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS)); } /** * Overrides the frequenies in SingleScanSetting * * @param settings the SingleScanSettings will be overrided. * @param freqs new frequencies of SingleScanSettings */ @Nullable public void overrideFreqsForSingleScanSettingsIfNecessary( @Nullable SingleScanSettings settings, @Nullable Set<Integer> freqs) { if (!isUsePredictedScanningChannels() || settings == null || freqs == null || freqs.size() == 0) { return; } if (settings.channelSettings == null) { settings.channelSettings = new ArrayList<>(); } else { settings.channelSettings.clear(); } for (int freq : freqs) { if (freq > 0) { ChannelSettings channel = new ChannelSettings(); channel.frequency = freq; settings.channelSettings.add(channel); } } // Monitor connection after last override scan request. if (mIsNoConnectionAlarmSet) { mAlarmManager.cancel(mNoConnectionTimeoutCallback); } mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, getMockableElapsedRealtime() + OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS, NO_CONNECTION_TIMEOUT_ALARM_TAG, mNoConnectionTimeoutCallback, mEventHandler); mIsNoConnectionAlarmSet = true; } /** * Returns the predicted scanning chcnnels set. */ @NonNull public Set<Integer> getPredictedScanningChannels() { Set<Integer> predictedScanChannels = new HashSet<>(); if (!isUsePredictedScanningChannels()) { Log.d(INSTANT_WIFI_TAG, "Drop, size: " + mConnectedWifiNetworkList.size()); return predictedScanChannels; } for (WifiNetwork network : mConnectedWifiNetworkList) { for (int connectedFrequency : network.getLastTwoConnectedFrequencies()) { if (connectedFrequency > 0) { predictedScanChannels.add(connectedFrequency); Log.d(INSTANT_WIFI_TAG, "Add channel: " + connectedFrequency + " to predicted channel"); } } } return predictedScanChannels; } } wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java +22 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,8 @@ public class WifiNl80211Manager { // Cached wificond binder handlers. private IWificond mWificond; private Context mContext; private InstantWifi mInstantWifi; private WificondEventHandler mWificondEventHandler = new WificondEventHandler(); private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>(); private HashMap<String, IApInterface> mApInterfaces = new HashMap<>(); Loading Loading @@ -172,6 +174,12 @@ public class WifiNl80211Manager { void onPnoRequestFailed(); } /** @hide */ @VisibleForTesting protected InstantWifi getInstantWifiMockable() { return mInstantWifi; } /** @hide */ @VisibleForTesting public class WificondEventHandler extends IWificondEventCallback.Stub { Loading Loading @@ -419,6 +427,7 @@ public class WifiNl80211Manager { public WifiNl80211Manager(Context context) { mAlarmManager = context.getSystemService(AlarmManager.class); mEventHandler = new Handler(context.getMainLooper()); mContext = context; } /** Loading @@ -434,6 +443,7 @@ public class WifiNl80211Manager { if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); } mContext = context; } /** @hide */ Loading @@ -441,6 +451,7 @@ public class WifiNl80211Manager { public WifiNl80211Manager(Context context, IWificond wificond) { this(context); mWificond = wificond; mContext = context; } /** @hide */ Loading Loading @@ -744,6 +755,9 @@ public class WifiNl80211Manager { Log.e(TAG, "Failed to refresh wificond scanner due to remote exception"); } if (getInstantWifiMockable() == null) { mInstantWifi = new InstantWifi(mContext, mAlarmManager, mEventHandler); } return true; } Loading Loading @@ -1071,6 +1085,10 @@ public class WifiNl80211Manager { if (settings == null) { return false; } if (getInstantWifiMockable() != null) { getInstantWifiMockable().overrideFreqsForSingleScanSettingsIfNecessary(settings, getInstantWifiMockable().getPredictedScanningChannels()); } try { return scannerImpl.scan(settings); } catch (RemoteException e1) { Loading Loading @@ -1115,6 +1133,10 @@ public class WifiNl80211Manager { if (settings == null) { return WifiScanner.REASON_INVALID_ARGS; } if (getInstantWifiMockable() != null) { getInstantWifiMockable().overrideFreqsForSingleScanSettingsIfNecessary(settings, getInstantWifiMockable().getPredictedScanningChannels()); } try { int status = scannerImpl.scanRequest(settings); return toFrameworkScanStatusCode(status); Loading wifi/tests/src/android/net/wifi/nl80211/InstantWifiTest.java 0 → 100644 +256 −0 File added.Preview size limit exceeded, changes collapsed. Show changes wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java +54 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net.wifi.nl80211; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; Loading @@ -26,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; Loading @@ -37,6 +39,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.app.test.TestAlarmManager; import android.content.Context; import android.net.MacAddress; Loading Loading @@ -104,6 +107,9 @@ public class WifiNl80211ManagerTest { private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener2; @Mock private Context mContext; @Mock private InstantWifi mMockInstantWifi; private TestLooper mLooper; private TestAlarmManager mTestAlarmManager; private AlarmManager mAlarmManager; Loading Loading @@ -167,6 +173,17 @@ public class WifiNl80211ManagerTest { 0x00, 0x00 }; private class WifiNl80211ManagerSpy extends WifiNl80211Manager { WifiNl80211ManagerSpy(Context context, IWificond wificond) { super(context, wificond); } @Override protected InstantWifi getInstantWifiMockable() { return mMockInstantWifi; } } @Before public void setUp() throws Exception { // Setup mocks for successful WificondControl operation. Failure case mocks should be Loading @@ -181,6 +198,8 @@ public class WifiNl80211ManagerTest { mLooper = new TestLooper(); when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); doNothing().when(mMockInstantWifi).overrideFreqsForSingleScanSettingsIfNecessary( any(), any()); when(mWificond.asBinder()).thenReturn(mWifiCondBinder); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mWificond.createClientInterface(any())).thenReturn(mClientInterface); Loading @@ -189,7 +208,7 @@ public class WifiNl80211ManagerTest { when(mWificond.tearDownApInterface(any())).thenReturn(true); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); mWificondControl = new WifiNl80211Manager(mContext, mWificond); mWificondControl = new WifiNl80211ManagerSpy(mContext, mWificond); mWificondEventHandler = mWificondControl.getWificondEventHandler(); assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, Loading Loading @@ -1159,6 +1178,40 @@ public class WifiNl80211ManagerTest { verify(mWificond).notifyCountryCodeChanged(); } @Test public void testInstantWifi() throws Exception { doAnswer(new AnswerWithArguments() { public void answer(SingleScanSettings settings, Set<Integer> freqs) { if (settings.channelSettings == null) { settings.channelSettings = new ArrayList<>(); } else { settings.channelSettings.clear(); } for (int freq : freqs) { if (freq > 0) { ChannelSettings channel = new ChannelSettings(); channel.frequency = freq; settings.channelSettings.add(channel); } } } }).when(mMockInstantWifi).overrideFreqsForSingleScanSettingsIfNecessary( any(), any()); Set<Integer> testPredictedChannelsSet = Set.of(2412, 5745); assertNotEquals(testPredictedChannelsSet, SCAN_FREQ_SET); when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); when(mMockInstantWifi.getPredictedScanningChannels()).thenReturn(testPredictedChannelsSet); // Trigger scan to check scan settings are changed assertTrue(mWificondControl.startScan( TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); verify(mMockInstantWifi).getPredictedScanningChannels(); verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( IWifiScannerImpl.SCAN_TYPE_LOW_POWER, testPredictedChannelsSet, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null))); } // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it // matches the provided frequency set and ssid set. private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> { Loading Loading
wifi/java/src/android/net/wifi/nl80211/InstantWifi.java 0 → 100644 +318 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 android.net.wifi.nl80211; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; /** * @hide */ public class InstantWifi { private static final String INSTANT_WIFI_TAG = "InstantWifi"; private static final int OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS = 1000; private static final int WIFI_NETWORK_EXPIRED_MS = 7 * 24 * 60 * 60 * 1000; // a week private static final String NO_CONNECTION_TIMEOUT_ALARM_TAG = INSTANT_WIFI_TAG + " No Connection Timeout"; private Context mContext; private AlarmManager mAlarmManager; private Handler mEventHandler; private ConnectivityManager mConnectivityManager; private WifiManager mWifiManager; private PowerManager mPowerManager; private long mLastWifiOnSinceBootMs; private long mLastScreenOnSinceBootMs; private boolean mIsWifiConnected = false; private boolean mScreenOn = false; private boolean mWifiEnabled = false; private boolean mIsNoConnectionAlarmSet = false; private ArrayList<WifiNetwork> mConnectedWifiNetworkList = new ArrayList<>(); private AlarmManager.OnAlarmListener mNoConnectionTimeoutCallback = new AlarmManager.OnAlarmListener() { public void onAlarm() { Log.i(INSTANT_WIFI_TAG, "Timed out waiting for wifi connection"); mIsNoConnectionAlarmSet = false; mWifiManager.startScan(); } }; public InstantWifi(Context context, AlarmManager alarmManager, Handler eventHandler) { mContext = context; mAlarmManager = alarmManager; mEventHandler = eventHandler; mWifiManager = mContext.getSystemService(WifiManager.class); mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mConnectivityManager.registerNetworkCallback( new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build(), new WifiNetworkCallback()); // System power service was initialized before wifi nl80211 service. mPowerManager = mContext.getSystemService(PowerManager.class); IntentFilter screenEventfilter = new IntentFilter(); screenEventfilter.addAction(Intent.ACTION_SCREEN_ON); screenEventfilter.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_ON)) { if (!mScreenOn) { mLastScreenOnSinceBootMs = getMockableElapsedRealtime(); } mScreenOn = true; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; } Log.d(INSTANT_WIFI_TAG, "mScreenOn is changed to " + mScreenOn); } }, screenEventfilter, null, mEventHandler); mScreenOn = mPowerManager.isInteractive(); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); mWifiEnabled = state == WifiManager.WIFI_STATE_ENABLED; if (mWifiEnabled) { mLastWifiOnSinceBootMs = getMockableElapsedRealtime(); } Log.d(INSTANT_WIFI_TAG, "mWifiEnabled is changed to " + mWifiEnabled); } }, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION), null, mEventHandler); } @VisibleForTesting protected long getMockableElapsedRealtime() { return SystemClock.elapsedRealtime(); } private class WifiNetwork { private final int mNetId; private Set<Integer> mConnectedFrequencies = new HashSet<Integer>(); private int[] mLastTwoConnectedFrequencies = new int[2]; private long mLastConnectedTimeMillis; WifiNetwork(int netId) { mNetId = netId; } public int getNetId() { return mNetId; } public boolean addConnectedFrequency(int channelFrequency) { mLastConnectedTimeMillis = getMockableElapsedRealtime(); if (mLastTwoConnectedFrequencies[0] != channelFrequency && mLastTwoConnectedFrequencies[1] != channelFrequency) { mLastTwoConnectedFrequencies[0] = mLastTwoConnectedFrequencies[1]; mLastTwoConnectedFrequencies[1] = channelFrequency; } return mConnectedFrequencies.add(channelFrequency); } public Set<Integer> getConnectedFrequencies() { return mConnectedFrequencies; } public int[] getLastTwoConnectedFrequencies() { if ((getMockableElapsedRealtime() - mLastConnectedTimeMillis) > WIFI_NETWORK_EXPIRED_MS) { return new int[0]; } return mLastTwoConnectedFrequencies; } public long getLastConnectedTimeMillis() { return mLastConnectedTimeMillis; } } private class WifiNetworkCallback extends NetworkCallback { @Override public void onAvailable(@NonNull Network network) { } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (networkCapabilities != null && network != null) { WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo(); if (wifiInfo == null || mWifiManager == null) { return; } WifiConfiguration config = mWifiManager.getPrivilegedConnectedNetwork(); if (config == null) { return; } final int currentNetworkId = config.networkId; final int connectecFrequency = wifiInfo.getFrequency(); if (connectecFrequency < 0 || currentNetworkId < 0) { return; } mIsWifiConnected = true; if (mIsNoConnectionAlarmSet) { mAlarmManager.cancel(mNoConnectionTimeoutCallback); } Log.d(INSTANT_WIFI_TAG, "Receive Wifi is connected, freq = " + connectecFrequency + " and currentNetworkId : " + currentNetworkId + ", wifiinfo = " + wifiInfo); boolean isExist = false; for (WifiNetwork wifiNetwork : mConnectedWifiNetworkList) { if (wifiNetwork.getNetId() == currentNetworkId) { if (wifiNetwork.addConnectedFrequency(connectecFrequency)) { Log.d(INSTANT_WIFI_TAG, "Update connected frequency: " + connectecFrequency + " to Network currentNetworkId : " + currentNetworkId); } isExist = true; } } if (!isExist) { WifiNetwork currentNetwork = new WifiNetwork(currentNetworkId); currentNetwork.addConnectedFrequency(connectecFrequency); if (mConnectedWifiNetworkList.size() < 5) { mConnectedWifiNetworkList.add(currentNetwork); } else { ArrayList<WifiNetwork> lastConnectedWifiNetworkList = new ArrayList<>(); WifiNetwork legacyNetwork = mConnectedWifiNetworkList.get(0); for (WifiNetwork connectedNetwork : mConnectedWifiNetworkList) { if (connectedNetwork.getNetId() == legacyNetwork.getNetId()) { continue; } // Keep the used recently network in the last connected list if (connectedNetwork.getLastConnectedTimeMillis() > legacyNetwork.getLastConnectedTimeMillis()) { lastConnectedWifiNetworkList.add(connectedNetwork); } else { lastConnectedWifiNetworkList.add(legacyNetwork); legacyNetwork = connectedNetwork; } } mConnectedWifiNetworkList = lastConnectedWifiNetworkList; } } } } @Override public void onLost(@NonNull Network network) { mIsWifiConnected = false; } } /** * Returns whether or not the scan freqs should be overrided by using predicted channels. */ public boolean isUsePredictedScanningChannels() { if (mIsWifiConnected || mConnectedWifiNetworkList.size() == 0 || !mWifiManager.isWifiEnabled() || !mPowerManager.isInteractive()) { return false; } if (!mWifiEnabled || !mScreenOn) { Log.d(INSTANT_WIFI_TAG, "WiFi/Screen State mis-match, run instant Wifi anyway!"); return true; } return (((getMockableElapsedRealtime() - mLastWifiOnSinceBootMs) < OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS) || ((getMockableElapsedRealtime() - mLastScreenOnSinceBootMs) < OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS)); } /** * Overrides the frequenies in SingleScanSetting * * @param settings the SingleScanSettings will be overrided. * @param freqs new frequencies of SingleScanSettings */ @Nullable public void overrideFreqsForSingleScanSettingsIfNecessary( @Nullable SingleScanSettings settings, @Nullable Set<Integer> freqs) { if (!isUsePredictedScanningChannels() || settings == null || freqs == null || freqs.size() == 0) { return; } if (settings.channelSettings == null) { settings.channelSettings = new ArrayList<>(); } else { settings.channelSettings.clear(); } for (int freq : freqs) { if (freq > 0) { ChannelSettings channel = new ChannelSettings(); channel.frequency = freq; settings.channelSettings.add(channel); } } // Monitor connection after last override scan request. if (mIsNoConnectionAlarmSet) { mAlarmManager.cancel(mNoConnectionTimeoutCallback); } mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, getMockableElapsedRealtime() + OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS, NO_CONNECTION_TIMEOUT_ALARM_TAG, mNoConnectionTimeoutCallback, mEventHandler); mIsNoConnectionAlarmSet = true; } /** * Returns the predicted scanning chcnnels set. */ @NonNull public Set<Integer> getPredictedScanningChannels() { Set<Integer> predictedScanChannels = new HashSet<>(); if (!isUsePredictedScanningChannels()) { Log.d(INSTANT_WIFI_TAG, "Drop, size: " + mConnectedWifiNetworkList.size()); return predictedScanChannels; } for (WifiNetwork network : mConnectedWifiNetworkList) { for (int connectedFrequency : network.getLastTwoConnectedFrequencies()) { if (connectedFrequency > 0) { predictedScanChannels.add(connectedFrequency); Log.d(INSTANT_WIFI_TAG, "Add channel: " + connectedFrequency + " to predicted channel"); } } } return predictedScanChannels; } }
wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java +22 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,8 @@ public class WifiNl80211Manager { // Cached wificond binder handlers. private IWificond mWificond; private Context mContext; private InstantWifi mInstantWifi; private WificondEventHandler mWificondEventHandler = new WificondEventHandler(); private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>(); private HashMap<String, IApInterface> mApInterfaces = new HashMap<>(); Loading Loading @@ -172,6 +174,12 @@ public class WifiNl80211Manager { void onPnoRequestFailed(); } /** @hide */ @VisibleForTesting protected InstantWifi getInstantWifiMockable() { return mInstantWifi; } /** @hide */ @VisibleForTesting public class WificondEventHandler extends IWificondEventCallback.Stub { Loading Loading @@ -419,6 +427,7 @@ public class WifiNl80211Manager { public WifiNl80211Manager(Context context) { mAlarmManager = context.getSystemService(AlarmManager.class); mEventHandler = new Handler(context.getMainLooper()); mContext = context; } /** Loading @@ -434,6 +443,7 @@ public class WifiNl80211Manager { if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); } mContext = context; } /** @hide */ Loading @@ -441,6 +451,7 @@ public class WifiNl80211Manager { public WifiNl80211Manager(Context context, IWificond wificond) { this(context); mWificond = wificond; mContext = context; } /** @hide */ Loading Loading @@ -744,6 +755,9 @@ public class WifiNl80211Manager { Log.e(TAG, "Failed to refresh wificond scanner due to remote exception"); } if (getInstantWifiMockable() == null) { mInstantWifi = new InstantWifi(mContext, mAlarmManager, mEventHandler); } return true; } Loading Loading @@ -1071,6 +1085,10 @@ public class WifiNl80211Manager { if (settings == null) { return false; } if (getInstantWifiMockable() != null) { getInstantWifiMockable().overrideFreqsForSingleScanSettingsIfNecessary(settings, getInstantWifiMockable().getPredictedScanningChannels()); } try { return scannerImpl.scan(settings); } catch (RemoteException e1) { Loading Loading @@ -1115,6 +1133,10 @@ public class WifiNl80211Manager { if (settings == null) { return WifiScanner.REASON_INVALID_ARGS; } if (getInstantWifiMockable() != null) { getInstantWifiMockable().overrideFreqsForSingleScanSettingsIfNecessary(settings, getInstantWifiMockable().getPredictedScanningChannels()); } try { int status = scannerImpl.scanRequest(settings); return toFrameworkScanStatusCode(status); Loading
wifi/tests/src/android/net/wifi/nl80211/InstantWifiTest.java 0 → 100644 +256 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java +54 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net.wifi.nl80211; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; Loading @@ -26,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; Loading @@ -37,6 +39,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.app.test.TestAlarmManager; import android.content.Context; import android.net.MacAddress; Loading Loading @@ -104,6 +107,9 @@ public class WifiNl80211ManagerTest { private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener2; @Mock private Context mContext; @Mock private InstantWifi mMockInstantWifi; private TestLooper mLooper; private TestAlarmManager mTestAlarmManager; private AlarmManager mAlarmManager; Loading Loading @@ -167,6 +173,17 @@ public class WifiNl80211ManagerTest { 0x00, 0x00 }; private class WifiNl80211ManagerSpy extends WifiNl80211Manager { WifiNl80211ManagerSpy(Context context, IWificond wificond) { super(context, wificond); } @Override protected InstantWifi getInstantWifiMockable() { return mMockInstantWifi; } } @Before public void setUp() throws Exception { // Setup mocks for successful WificondControl operation. Failure case mocks should be Loading @@ -181,6 +198,8 @@ public class WifiNl80211ManagerTest { mLooper = new TestLooper(); when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); doNothing().when(mMockInstantWifi).overrideFreqsForSingleScanSettingsIfNecessary( any(), any()); when(mWificond.asBinder()).thenReturn(mWifiCondBinder); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mWificond.createClientInterface(any())).thenReturn(mClientInterface); Loading @@ -189,7 +208,7 @@ public class WifiNl80211ManagerTest { when(mWificond.tearDownApInterface(any())).thenReturn(true); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); mWificondControl = new WifiNl80211Manager(mContext, mWificond); mWificondControl = new WifiNl80211ManagerSpy(mContext, mWificond); mWificondEventHandler = mWificondControl.getWificondEventHandler(); assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, Loading Loading @@ -1159,6 +1178,40 @@ public class WifiNl80211ManagerTest { verify(mWificond).notifyCountryCodeChanged(); } @Test public void testInstantWifi() throws Exception { doAnswer(new AnswerWithArguments() { public void answer(SingleScanSettings settings, Set<Integer> freqs) { if (settings.channelSettings == null) { settings.channelSettings = new ArrayList<>(); } else { settings.channelSettings.clear(); } for (int freq : freqs) { if (freq > 0) { ChannelSettings channel = new ChannelSettings(); channel.frequency = freq; settings.channelSettings.add(channel); } } } }).when(mMockInstantWifi).overrideFreqsForSingleScanSettingsIfNecessary( any(), any()); Set<Integer> testPredictedChannelsSet = Set.of(2412, 5745); assertNotEquals(testPredictedChannelsSet, SCAN_FREQ_SET); when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); when(mMockInstantWifi.getPredictedScanningChannels()).thenReturn(testPredictedChannelsSet); // Trigger scan to check scan settings are changed assertTrue(mWificondControl.startScan( TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); verify(mMockInstantWifi).getPredictedScanningChannels(); verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( IWifiScannerImpl.SCAN_TYPE_LOW_POWER, testPredictedChannelsSet, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null))); } // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it // matches the provided frequency set and ssid set. private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> { Loading