Support for hotspot client visibility.

Adding support for visibility into clients that connect to an active hotspot.

Bug: 137309578
Test: atest FrameworksNetTests:com.android.server.connectivity.TetheringTest
atest FrameworksWifiApiTests:android.net.wifi.WifiManagerTest
atest FrameworksWifiApiTests:android.net.wifi.WifiClientTest
Tested manually on Hawk.

Change-Id: I1caeb10bc50202873e760a76b346bccd941e2574
This commit is contained in:
James Mattis
2019-10-04 17:46:12 -07:00
parent ef151738b7
commit aefe78c409
10 changed files with 245 additions and 53 deletions

View File

@@ -4733,6 +4733,13 @@ package android.net.wifi {
field @Deprecated public byte id;
}
public final class WifiClient implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.net.MacAddress getMacAddress();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiClient> CREATOR;
}
@Deprecated public class WifiConfiguration implements android.os.Parcelable {
method @Deprecated public boolean hasNoInternetAccess();
method @Deprecated public boolean isEphemeral();

View File

@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy;
import android.app.ActivityManager;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.UserManager;
@@ -29,11 +30,13 @@ import com.android.systemui.dagger.qualifiers.MainHandler;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Controller used to retrieve information related to a hotspot.
*/
@Singleton
public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
@@ -48,11 +51,12 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
private final Context mContext;
private int mHotspotState;
private int mNumConnectedDevices;
private volatile int mNumConnectedDevices;
private boolean mWaitingForTerminalState;
private boolean mListening;
/**
* Controller used to retrieve information related to a hotspot.
*/
@Inject
public HotspotControllerImpl(Context context, @MainHandler Handler mainHandler) {
@@ -96,7 +100,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
/**
* Adds {@code callback} to the controller. The controller will update the callback on state
* changes. It will immediately trigger the callback added to notify current state.
* @param callback
*/
@Override
public void addCallback(Callback callback) {
@@ -110,8 +113,8 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
mWifiManager.registerSoftApCallback(this, mMainHandler);
} else {
// mWifiManager#registerSoftApCallback triggers a call to
// onNumClientsChanged on the Main Handler. In order to always update the
// callback on added, we make this call when adding callbacks after the
// onConnectedClientsChanged on the Main Handler. In order to always update
// the callback on added, we make this call when adding callbacks after the
// first.
mMainHandler.post(() ->
callback.onHotspotChanged(isHotspotEnabled(),
@@ -232,8 +235,8 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
}
@Override
public void onNumClientsChanged(int numConnectedDevices) {
mNumConnectedDevices = numConnectedDevices;
public void onConnectedClientsChanged(List<WifiClient> clients) {
mNumConnectedDevices = clients.size();
fireHotspotChangedCallback();
}
}

View File

@@ -42,6 +42,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -67,7 +69,8 @@ public class HotspotControllerImplTest extends SysuiTestCase {
mContext.addMockSystemService(WifiManager.class, mWifiManager);
doAnswer((InvocationOnMock invocation) -> {
((WifiManager.SoftApCallback) invocation.getArgument(0)).onNumClientsChanged(1);
((WifiManager.SoftApCallback) invocation.getArgument(0))
.onConnectedClientsChanged(new ArrayList<>());
return null;
}).when(mWifiManager).registerSoftApCallback(any(WifiManager.SoftApCallback.class),
any(Handler.class));

View File

@@ -16,6 +16,8 @@
package android.net.wifi;
import android.net.wifi.WifiClient;
/**
* Interface for Soft AP callback.
*
@@ -36,9 +38,9 @@ oneway interface ISoftApCallback
void onStateChanged(int state, int failureReason);
/**
* Service to manager callback providing number of connected clients.
* Service to manager callback providing connected client's information.
*
* @param numClients number of connected clients
* @param clients the currently connected clients
*/
void onNumClientsChanged(int numClients);
void onConnectedClientsChanged(in List<WifiClient> clients);
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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;
@JavaOnlyStableParcelable parcelable WifiClient;

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2019 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;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import java.util.Objects;
/** @hide */
@SystemApi
public final class WifiClient implements Parcelable {
private final MacAddress mMacAddress;
/**
* The mac address of this client.
*/
@NonNull
public MacAddress getMacAddress() {
return mMacAddress;
}
private WifiClient(Parcel in) {
mMacAddress = in.readParcelable(null);
}
/** @hide */
public WifiClient(@NonNull MacAddress macAddress) {
Preconditions.checkNotNull(macAddress, "mMacAddress must not be null.");
this.mMacAddress = macAddress;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mMacAddress, flags);
}
@NonNull
public static final Creator<WifiClient> CREATOR = new Creator<WifiClient>() {
public WifiClient createFromParcel(Parcel in) {
return new WifiClient(in);
}
public WifiClient[] newArray(int size) {
return new WifiClient[size];
}
};
@NonNull
@Override
public String toString() {
return "WifiClient{"
+ "mMacAddress=" + mMacAddress
+ '}';
}
@Override
public boolean equals(@NonNull Object o) {
if (this == o) return true;
if (!(o instanceof WifiClient)) return false;
WifiClient client = (WifiClient) o;
return mMacAddress.equals(client.mMacAddress);
}
@Override
public int hashCode() {
return Objects.hash(mMacAddress);
}
}

View File

@@ -3254,21 +3254,22 @@ public class WifiManager {
/**
* Called when soft AP state changes.
*
* @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED},
* {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
* {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
* @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED},
* {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
* {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
* @param failureReason reason when in failed state. One of
* {@link #SAP_START_FAILURE_GENERAL}, {@link #SAP_START_FAILURE_NO_CHANNEL}
* {@link #SAP_START_FAILURE_GENERAL},
* {@link #SAP_START_FAILURE_NO_CHANNEL}
*/
public abstract void onStateChanged(@WifiApState int state,
void onStateChanged(@WifiApState int state,
@SapStartFailure int failureReason);
/**
* Called when number of connected clients to soft AP changes.
* Called when the connected clients to soft AP changes.
*
* @param numClients number of connected clients
* @param clients the currently connected clients
*/
public abstract void onNumClientsChanged(int numClients);
void onConnectedClientsChanged(@NonNull List<WifiClient> clients);
}
/**
@@ -3291,18 +3292,21 @@ public class WifiManager {
Log.v(TAG, "SoftApCallbackProxy: onStateChanged: state=" + state
+ ", failureReason=" + failureReason);
}
mHandler.post(() -> {
mCallback.onStateChanged(state, failureReason);
});
}
@Override
public void onNumClientsChanged(int numClients) {
public void onConnectedClientsChanged(List<WifiClient> clients) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "SoftApCallbackProxy: onNumClientsChanged: numClients=" + numClients);
Log.v(TAG, "SoftApCallbackProxy: onConnectedClientsChanged: clients="
+ clients.size() + " clients");
}
mHandler.post(() -> {
mCallback.onNumClientsChanged(numClients);
mCallback.onConnectedClientsChanged(clients);
});
}
}

View File

@@ -49,6 +49,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
core-test-rules \
guava \
mockito-target-minus-junit4 \
net-tests-utils \
frameworks-base-testutils \
truth-prebuilt \

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2019 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;
import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.net.MacAddress;
import androidx.test.filters.SmallTest;
import org.junit.Test;
/**
* Unit tests for {@link android.net.wifi.WifiClient}.
*/
@SmallTest
public class WifiClientTest {
private static final String INTERFACE_NAME = "wlan0";
private static final String MAC_ADDRESS_STRING = "00:0a:95:9d:68:16";
private static final MacAddress MAC_ADDRESS = MacAddress.fromString(MAC_ADDRESS_STRING);
/**
* Verify parcel write/read with WifiClient.
*/
@Test
public void testWifiClientParcelWriteRead() throws Exception {
WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS);
assertParcelSane(writeWifiClient, 1);
}
/**
* Verify equals with WifiClient.
*/
@Test
public void testWifiClientEquals() throws Exception {
WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS);
WifiClient writeWifiClientEquals = new WifiClient(MAC_ADDRESS);
assertEquals(writeWifiClient, writeWifiClientEquals);
assertEquals(writeWifiClient.hashCode(), writeWifiClientEquals.hashCode());
assertFieldCountEquals(1, WifiClient.class);
}
/**
* Verify not-equals with WifiClient.
*/
@Test
public void testWifiClientNotEquals() throws Exception {
final MacAddress macAddressNotEquals = MacAddress.fromString("00:00:00:00:00:00");
WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS);
WifiClient writeWifiClientNotEquals = new WifiClient(macAddressNotEquals);
assertNotEquals(writeWifiClient, writeWifiClientNotEquals);
assertNotEquals(writeWifiClient.hashCode(), writeWifiClientNotEquals.hashCode());
}
}

View File

@@ -99,8 +99,7 @@ public class WifiManagerTest {
private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"};
@Mock Context mContext;
@Mock
android.net.wifi.IWifiManager mWifiService;
@Mock android.net.wifi.IWifiManager mWifiService;
@Mock ApplicationInfo mApplicationInfo;
@Mock WifiConfiguration mApConfig;
@Mock SoftApCallback mSoftApCallback;
@@ -115,7 +114,8 @@ public class WifiManagerTest {
private TestLooper mLooper;
private WifiManager mWifiManager;
@Before public void setUp() throws Exception {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLooper = new TestLooper();
mHandler = spy(new Handler(mLooper.getLooper()));
@@ -210,7 +210,7 @@ public class WifiManagerTest {
@Test
public void testCreationOfLocalOnlyHotspotSubscription() throws Exception {
try (WifiManager.LocalOnlyHotspotSubscription sub =
mWifiManager.new LocalOnlyHotspotSubscription()) {
mWifiManager.new LocalOnlyHotspotSubscription()) {
sub.close();
}
}
@@ -752,17 +752,17 @@ public class WifiManagerTest {
* Verify client-provided callback is being called through callback proxy
*/
@Test
public void softApCallbackProxyCallsOnNumClientsChanged() throws Exception {
public void softApCallbackProxyCallsOnConnectedClientsChanged() throws Exception {
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
final int testNumClients = 3;
callbackCaptor.getValue().onNumClientsChanged(testNumClients);
final List<WifiClient> testClients = new ArrayList();
callbackCaptor.getValue().onConnectedClientsChanged(testClients);
mLooper.dispatchAll();
verify(mSoftApCallback).onNumClientsChanged(testNumClients);
verify(mSoftApCallback).onConnectedClientsChanged(testClients);
}
/*
@@ -776,14 +776,14 @@ public class WifiManagerTest {
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
final int testNumClients = 5;
final List<WifiClient> testClients = new ArrayList();
callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLING, 0);
callbackCaptor.getValue().onNumClientsChanged(testNumClients);
callbackCaptor.getValue().onConnectedClientsChanged(testClients);
callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
mLooper.dispatchAll();
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0);
verify(mSoftApCallback).onNumClientsChanged(testNumClients);
verify(mSoftApCallback).onConnectedClientsChanged(testClients);
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
}
@@ -1020,8 +1020,8 @@ public class WifiManagerTest {
verifyNoMoreInteractions(mWifiService);
}
/**
i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Verify that a call to cancel WPS immediately returns a failure.
*/
@Test
public void testCancelWpsImmediatelyFailsWithCallback() {
@@ -1324,7 +1324,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Verify getting the factory MAC address.
* @throws Exception
*/
@Test
public void testGetFactoryMacAddress() throws Exception {
@@ -1371,7 +1370,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isEnhancedOpenSupported
* @throws Exception
*/
@Test
public void testIsEnhancedOpenSupported() throws Exception {
@@ -1385,7 +1383,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isWpa3SaeSupported
* @throws Exception
*/
@Test
public void testIsWpa3SaeSupported() throws Exception {
@@ -1399,7 +1396,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isWpa3SuiteBSupported
* @throws Exception
*/
@Test
public void testIsWpa3SuiteBSupported() throws Exception {
@@ -1413,7 +1409,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isEasyConnectSupported
* @throws Exception
*/
@Test
public void testIsEasyConnectSupported() throws Exception {
@@ -1427,7 +1422,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#addNetwork(WifiConfiguration)}
* @throws Exception
*/
@Test
public void testAddNetwork() throws Exception {
@@ -1444,7 +1438,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#addNetwork(WifiConfiguration)}
* @throws Exception
*/
@Test
public void testUpdateNetwork() throws Exception {
@@ -1466,7 +1459,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#enableNetwork(int, boolean)}
* @throws Exception
*/
@Test
public void testEnableNetwork() throws Exception {
@@ -1478,7 +1470,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#disableNetwork(int)}
* @throws Exception
*/
@Test
public void testDisableNetwork() throws Exception {
@@ -1500,7 +1491,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#disconnect()}
* @throws Exception
*/
@Test
public void testDisconnect() throws Exception {
@@ -1511,7 +1501,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#reconnect()}
* @throws Exception
*/
@Test
public void testReconnect() throws Exception {
@@ -1522,7 +1511,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#reassociate()}
* @throws Exception
*/
@Test
public void testReassociate() throws Exception {
@@ -1533,7 +1521,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getSupportedFeatures()}
* @throws Exception
*/
@Test
public void testGetSupportedFeatures() throws Exception {
@@ -1560,7 +1547,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getControllerActivityEnergyInfo()}
* @throws Exception
*/
@Test
public void testGetControllerActivityEnergyInfo() throws Exception {
@@ -1573,7 +1559,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getConnectionInfo()}
* @throws Exception
*/
@Test
public void testGetConnectionInfo() throws Exception {
@@ -1585,7 +1570,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#isDualModeSupported()} ()}
* @throws Exception
*/
@Test
public void testIsDualModeSupported() throws Exception {
@@ -1596,7 +1580,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#isDualBandSupported()}
* @throws Exception
*/
@Test
public void testIsDualBandSupported() throws Exception {
@@ -1607,7 +1590,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getDhcpInfo()}
* @throws Exception
*/
@Test
public void testGetDhcpInfo() throws Exception {
@@ -1620,7 +1602,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#setWifiEnabled(boolean)}
* @throws Exception
*/
@Test
public void testSetWifiEnabled() throws Exception {