summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrik Fimml <patrikf@google.com>2019-10-30 14:03:18 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2019-10-30 14:03:18 +0000
commit2124587f8af4ca1f9f2787747dd65d7eb9db9708 (patch)
treec76172c6292f4e9ef387c82d16068d3d9bed9c08
parent39bb4b840175768cb6756a8ed8a76c48663cf897 (diff)
parentb062c908670fb4180f6935d0cac0927d11fe7b6b (diff)
Merge "Configurable SoftAP: Service implementation."
-rw-r--r--service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java15
-rw-r--r--service/java/com/android/server/wifi/SoftApManager.java39
-rw-r--r--service/java/com/android/server/wifi/SoftApModeConfiguration.java31
-rw-r--r--service/java/com/android/server/wifi/WifiApConfigStore.java78
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java142
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java2
-rw-r--r--tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java30
-rw-r--r--tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java43
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java61
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java185
10 files changed, 496 insertions, 130 deletions
diff --git a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java
index 222651875..aec6127d3 100644
--- a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java
+++ b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java
@@ -17,7 +17,9 @@
package com.android.server.wifi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.wifi.ILocalOnlyHotspotCallback;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
import android.os.Binder;
import android.os.IBinder;
@@ -30,17 +32,18 @@ import com.android.internal.util.Preconditions;
*
* @hide
*/
-public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient {
+class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient {
static final int HOTSPOT_NO_ERROR = -1;
private final int mPid;
private final ILocalOnlyHotspotCallback mCallback;
private final RequestingApplicationDeathCallback mDeathCallback;
+ private final SoftApConfiguration mCustomConfig;
/**
* Callback for use with LocalOnlyHotspot to unregister requesting applications upon death.
*/
- public interface RequestingApplicationDeathCallback {
+ interface RequestingApplicationDeathCallback {
/**
* Called when requesting app has died.
*/
@@ -48,10 +51,12 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient {
}
LocalOnlyHotspotRequestInfo(@NonNull ILocalOnlyHotspotCallback callback,
- @NonNull RequestingApplicationDeathCallback deathCallback) {
+ @NonNull RequestingApplicationDeathCallback deathCallback,
+ @Nullable SoftApConfiguration customConfig) {
mPid = Binder.getCallingPid();
mCallback = Preconditions.checkNotNull(callback);
mDeathCallback = Preconditions.checkNotNull(deathCallback);
+ mCustomConfig = customConfig;
try {
mCallback.asBinder().linkToDeath(this, 0);
@@ -109,4 +114,8 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient {
public int getPid() {
return mPid;
}
+
+ public SoftApConfiguration getCustomConfig() {
+ return mCustomConfig;
+ }
}
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index a89f4334e..c28d224ff 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -152,11 +152,16 @@ public class SoftApManager implements ActiveModeManager {
mModeListener = listener;
mSoftApCallback = callback;
mWifiApConfigStore = wifiApConfigStore;
- mApConfig = apConfig;
- if (mApConfig.getWifiConfiguration() == null) {
- WifiConfiguration wifiConfig = mWifiApConfigStore.getApConfiguration();
- mApConfig = new SoftApModeConfiguration(mApConfig.getTargetMode(), wifiConfig);
+ WifiConfiguration wifiConfig = apConfig.getWifiConfiguration();
+ // null is a valid input and means we use the user-configured tethering settings.
+ if (wifiConfig == null) {
+ wifiConfig = mWifiApConfigStore.getApConfiguration();
+ // may still be null if we fail to load the default config
}
+ if (wifiConfig != null) {
+ wifiConfig = mWifiApConfigStore.randomizeBssidIfUnset(mContext, wifiConfig);
+ }
+ mApConfig = new SoftApModeConfiguration(apConfig.getTargetMode(), wifiConfig);
mWifiMetrics = wifiMetrics;
mSarManager = sarManager;
mWifiDiagnostics = wifiDiagnostics;
@@ -263,27 +268,35 @@ public class SoftApManager implements ActiveModeManager {
}
private int setMacAddress() {
- boolean randomize = mContext.getResources().getBoolean(
- R.bool.config_wifi_ap_mac_randomization_supported);
- if (!randomize) {
- MacAddress mac = mWifiNative.getFactoryMacAddress(mApInterfaceName);
+ String macStr = mApConfig.getWifiConfiguration().BSSID;
+ MacAddress mac = null;
+ if (!TextUtils.isEmpty(macStr)) {
+ try {
+ mac = MacAddress.fromString(macStr);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "invalid MAC address: " + macStr);
+ return ERROR_GENERIC;
+ }
+ }
+
+ if (mac == null) {
+ // If no BSSID is explicitly requested, (re-)configure the factory MAC address. Some
+ // drivers may not support setting the MAC at all, so fail soft in this case.
+ mac = mWifiNative.getFactoryMacAddress(mApInterfaceName);
if (mac == null) {
Log.e(TAG, "failed to get factory MAC address");
return ERROR_GENERIC;
}
- // We're (re-)configuring the factory MAC address. Some drivers may not support setting
- // the MAC at all, so fail soft in this case.
if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) {
Log.w(TAG, "failed to reset to factory MAC address; continuing with current MAC");
}
return SUCCESS;
}
- // We're configuring a random MAC address. In this case, driver support is mandatory.
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ // We're configuring a random/custom MAC address. In this case, driver support is mandatory.
if (!mWifiNative.setMacAddress(mApInterfaceName, mac)) {
- Log.e(TAG, "failed to set random MAC address");
+ Log.e(TAG, "failed to set explicitly requested MAC address");
return ERROR_GENERIC;
}
return SUCCESS;
diff --git a/service/java/com/android/server/wifi/SoftApModeConfiguration.java b/service/java/com/android/server/wifi/SoftApModeConfiguration.java
index e8805602e..bfc9a6c85 100644
--- a/service/java/com/android/server/wifi/SoftApModeConfiguration.java
+++ b/service/java/com/android/server/wifi/SoftApModeConfiguration.java
@@ -17,19 +17,34 @@
package com.android.server.wifi;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+
+import com.android.internal.util.Preconditions;
+
+import javax.annotation.Nullable;
/**
* Object holding the parameters needed to start SoftAp mode.
- *
- * Initially, this will hold the WifiConfiguration and mode.
*/
-public class SoftApModeConfiguration {
- final int mTargetMode;
- final WifiConfiguration mConfig;
+class SoftApModeConfiguration {
+ /**
+ * Routing mode. Either {@link android.net.wifi.WifiManager#IFACE_IP_MODE_TETHERED}
+ * or {@link android.net.wifi.WifiManager#IFACE_IP_MODE_LOCAL_ONLY}.
+ */
+ private final int mTargetMode;
+
+ /**
+ * WifiConfiguration for internal use, or null if it hasn't been generated yet.
+ */
+ private final @Nullable WifiConfiguration mWifiConfig;
+
+ SoftApModeConfiguration(int targetMode, @Nullable WifiConfiguration config) {
+ Preconditions.checkArgument(
+ targetMode == WifiManager.IFACE_IP_MODE_TETHERED
+ || targetMode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
- SoftApModeConfiguration(int targetMode, WifiConfiguration config) {
mTargetMode = targetMode;
- mConfig = config;
+ mWifiConfig = config;
}
public int getTargetMode() {
@@ -37,6 +52,6 @@ public class SoftApModeConfiguration {
}
public WifiConfiguration getWifiConfiguration() {
- return mConfig;
+ return mWifiConfig;
}
}
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index 87a170c78..5b230bbc8 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -24,6 +24,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.MacAddress;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.Environment;
@@ -48,6 +50,8 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Random;
+import javax.annotation.Nullable;
+
/**
* Provides API for reading/writing soft access point configuration.
*/
@@ -163,7 +167,7 @@ public class WifiApConfigStore {
* Return the current soft access point configuration.
*/
public synchronized WifiConfiguration getApConfiguration() {
- WifiConfiguration config = apBandCheckConvert(mWifiApConfig);
+ WifiConfiguration config = sanitizePersistentApConfig(mWifiApConfig);
if (mWifiApConfig != config) {
Log.d(TAG, "persisted config was converted, need to resave it");
mWifiApConfig = config;
@@ -182,7 +186,7 @@ public class WifiApConfigStore {
if (config == null) {
mWifiApConfig = getDefaultApConfiguration();
} else {
- mWifiApConfig = apBandCheckConvert(config);
+ mWifiApConfig = sanitizePersistentApConfig(config);
}
persistConfigAndTriggerBackupManagerProxy(mWifiApConfig);
}
@@ -232,28 +236,38 @@ public class WifiApConfigStore {
.build();
}
- private WifiConfiguration apBandCheckConvert(WifiConfiguration config) {
+ private WifiConfiguration sanitizePersistentApConfig(WifiConfiguration config) {
+ WifiConfiguration convertedConfig = null;
+
+ // Persistent config may not set BSSID.
+ if (config.BSSID != null) {
+ convertedConfig = new WifiConfiguration(config);
+ convertedConfig.BSSID = null;
+ }
+
if (mRequiresApBandConversion) {
// some devices are unable to support 5GHz only operation, check for 5GHz and
// move to ANY if apBand conversion is required.
if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
Log.w(TAG, "Supplied ap config band was 5GHz only, converting to ANY");
- WifiConfiguration convertedConfig = new WifiConfiguration(config);
+ if (convertedConfig == null) {
+ convertedConfig = new WifiConfiguration(config);
+ }
convertedConfig.apBand = WifiConfiguration.AP_BAND_ANY;
convertedConfig.apChannel = AP_CHANNEL_DEFAULT;
- return convertedConfig;
}
} else {
// this is a single mode device, we do not support ANY. Convert all ANY to 5GHz
if (config.apBand == WifiConfiguration.AP_BAND_ANY) {
Log.w(TAG, "Supplied ap config band was ANY, converting to 5GHz");
- WifiConfiguration convertedConfig = new WifiConfiguration(config);
+ if (convertedConfig == null) {
+ convertedConfig = new WifiConfiguration(config);
+ }
convertedConfig.apBand = WifiConfiguration.AP_BAND_5GHZ;
convertedConfig.apChannel = AP_CHANNEL_DEFAULT;
- return convertedConfig;
}
}
- return config;
+ return convertedConfig == null ? config : convertedConfig;
}
private void persistConfigAndTriggerBackupManagerProxy(WifiConfiguration config) {
@@ -352,20 +366,56 @@ public class WifiApConfigStore {
return random.nextInt((RAND_SSID_INT_MAX - RAND_SSID_INT_MIN) + 1) + RAND_SSID_INT_MIN;
}
+ private static String generateLohsSsid(Context context) {
+ return context.getResources().getString(
+ R.string.wifi_localhotspot_configure_ssid_default) + "_"
+ + getRandomIntForDefaultSsid();
+ }
+
/**
* Generate a temporary WPA2 based configuration for use by the local only hotspot.
* This config is not persisted and will not be stored by the WifiApConfigStore.
*/
- public static WifiConfiguration generateLocalOnlyHotspotConfig(Context context, int apBand) {
+ public static WifiConfiguration generateLocalOnlyHotspotConfig(Context context, int apBand,
+ @Nullable SoftApConfiguration customConfig) {
WifiConfiguration config = new WifiConfiguration();
- config.SSID = context.getResources().getString(
- R.string.wifi_localhotspot_configure_ssid_default) + "_"
- + getRandomIntForDefaultSsid();
config.apBand = apBand;
- config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
config.networkId = WifiConfiguration.LOCAL_ONLY_NETWORK_ID;
- config.preSharedKey = generatePassword();
+
+ if (customConfig == null || customConfig.getSsid() == null) {
+ config.SSID = generateLohsSsid(context);
+ } else {
+ config.SSID = customConfig.getSsid();
+ }
+ if (customConfig == null) {
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ config.preSharedKey = generatePassword();
+ } else if (customConfig.getWpa2Passphrase() != null) {
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ config.preSharedKey = customConfig.getWpa2Passphrase();
+ } else {
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ }
+ if (customConfig != null && customConfig.getBssid() != null) {
+ config.BSSID = customConfig.getBssid().toString();
+ } else {
+ // use factory or random BSSID
+ config.BSSID = null;
+ }
+ return config;
+ }
+
+ /**
+ * @return a copy of the given WifiConfig with the BSSID randomized, unless a custom BSSID is
+ * already set.
+ */
+ WifiConfiguration randomizeBssidIfUnset(Context context, WifiConfiguration config) {
+ config = new WifiConfiguration(config);
+ if (config.BSSID == null && context.getResources().getBoolean(
+ R.bool.config_wifi_ap_mac_randomization_supported)) {
+ config.BSSID = MacAddress.createRandomUnicastAddress().toString();
+ }
return config;
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index e2bc1fd08..d2a4678ad 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -58,6 +58,7 @@ import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiConfiguration;
@@ -753,33 +754,37 @@ public class WifiServiceImpl extends BaseWifiService {
mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush();
- if (mTetheredSoftApTracker.setEnablingIfAllowed()) {
- if (!isConcurrentLohsAndTetheringSupported()) {
- // Take down LOHS if it is up.
- mLohsSoftApTracker.stopAll();
- }
- return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED);
+ if (!mTetheredSoftApTracker.setEnablingIfAllowed()) {
+ mLog.err("Tethering is already active.").flush();
+ return false;
}
- mLog.err("Tethering is already active.").flush();
- return false;
+
+ if (!isConcurrentLohsAndTetheringSupported()) {
+ // Take down LOHS if it is up.
+ mLohsSoftApTracker.stopAll();
+ }
+ return startSoftApInternal(new SoftApModeConfiguration(
+ WifiManager.IFACE_IP_MODE_TETHERED, wifiConfig));
}
/**
* Internal method to start softap mode. Callers of this method should have already checked
* proper permissions beyond the NetworkStack permission.
*/
- private boolean startSoftApInternal(WifiConfiguration wifiConfig, int mode) {
+ private boolean startSoftApInternal(SoftApModeConfiguration apConfig) {
mLog.trace("startSoftApInternal uid=% mode=%")
- .c(Binder.getCallingUid()).c(mode).flush();
+ .c(Binder.getCallingUid()).c(apConfig.getTargetMode()).flush();
- // null wifiConfig is a meaningful input for CMD_SET_AP
- if (wifiConfig == null || WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) {
- SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);
- mActiveModeWarden.startSoftAp(softApConfig);
- return true;
+ // null wifiConfig is a meaningful input for CMD_SET_AP; it means to use the persistent
+ // AP config.
+ WifiConfiguration wifiConfig = apConfig.getWifiConfiguration();
+ if (wifiConfig != null && !WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) {
+ Log.e(TAG, "Invalid WifiConfiguration");
+ return false;
}
- Log.e(TAG, "Invalid WifiConfiguration");
- return false;
+
+ mActiveModeWarden.startSoftAp(apConfig);
+ return true;
}
/**
@@ -919,15 +924,23 @@ public class WifiServiceImpl extends BaseWifiService {
}
/**
- * Lohs callback
+ * Implements LOHS behavior on top of the existing SoftAp API.
*/
private final class LohsSoftApTracker implements WifiManager.SoftApCallback {
@GuardedBy("mLocalOnlyHotspotRequests")
private final HashMap<Integer, LocalOnlyHotspotRequestInfo>
mLocalOnlyHotspotRequests = new HashMap<>();
+ /** Currently-active config, to be sent to shared clients registering later. */
+ @GuardedBy("mLocalOnlyHotspotRequests")
+ private SoftApModeConfiguration mActiveConfig = null;
+
+ /**
+ * Whether we are currently operating in exclusive mode (i.e. whether a custom config is
+ * active).
+ */
@GuardedBy("mLocalOnlyHotspotRequests")
- private WifiConfiguration mLocalOnlyHotspotConfig = null;
+ private boolean mIsExclusive = false;
@GuardedBy("mLocalOnlyHotspotRequests")
private String mLohsInterfaceName;
@@ -1067,36 +1080,55 @@ public class WifiServiceImpl extends BaseWifiService {
"Caller already has an active LocalOnlyHotspot request");
}
- // check current operating state and take action if needed
+ // Never accept exclusive requests (with custom configuration) at the same time as
+ // shared requests.
+ if (mActiveConfig != null) {
+ boolean requestIsExclusive = request.getCustomConfig() != null;
+ if (mIsExclusive || requestIsExclusive) {
+ return LocalOnlyHotspotCallback.ERROR_GENERIC;
+ }
+ }
+
+ // If a local-only AP is already active, send the current config.
if (mLohsInterfaceMode == WifiManager.IFACE_IP_MODE_LOCAL_ONLY) {
- // LOHS is already active, send out what is running
try {
mLog.trace("LOHS already up, trigger onStarted callback").flush();
- request.sendHotspotStartedMessage(mLocalOnlyHotspotConfig);
+ request.sendHotspotStartedMessage(mActiveConfig.getWifiConfiguration());
} catch (RemoteException e) {
return LocalOnlyHotspotCallback.ERROR_GENERIC;
}
- } else if (mLocalOnlyHotspotRequests.isEmpty()) {
- // this is the first request, then set up our config and start LOHS
- boolean is5Ghz = hasAutomotiveFeature(mContext)
- && mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_local_only_hotspot_5ghz)
- && is5GhzSupported();
-
- mLocalOnlyHotspotConfig =
- WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext,
- is5Ghz ? WifiConfiguration.AP_BAND_5GHZ
- : WifiConfiguration.AP_BAND_2GHZ);
-
- startSoftApInternal(mLocalOnlyHotspotConfig,
- WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
}
+ if (mLocalOnlyHotspotRequests.isEmpty()) {
+ startForFirstRequestLocked(request);
+ }
mLocalOnlyHotspotRequests.put(pid, request);
+
return LocalOnlyHotspotCallback.REQUEST_REGISTERED;
}
}
+ @GuardedBy("mLocalOnlyHotspotRequests")
+ private void startForFirstRequestLocked(LocalOnlyHotspotRequestInfo request) {
+ boolean is5Ghz = hasAutomotiveFeature(mContext)
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_local_only_hotspot_5ghz)
+ && is5GhzSupported();
+
+ int band = is5Ghz ? WifiConfiguration.AP_BAND_5GHZ
+ : WifiConfiguration.AP_BAND_2GHZ;
+
+ WifiConfiguration wifiConfig = WifiApConfigStore.generateLocalOnlyHotspotConfig(
+ mContext, band, request.getCustomConfig());
+
+ mActiveConfig = new SoftApModeConfiguration(
+ WifiManager.IFACE_IP_MODE_LOCAL_ONLY,
+ wifiConfig);
+ mIsExclusive = (request.getCustomConfig() != null);
+
+ startSoftApInternal(mActiveConfig);
+ }
+
/**
* Requests that any local-only hotspot be stopped.
*/
@@ -1130,7 +1162,6 @@ public class WifiServiceImpl extends BaseWifiService {
* Unregisters LocalOnlyHotspot request and stops the hotspot if needed.
*/
public void stopByRequest(LocalOnlyHotspotRequestInfo request) {
-
synchronized (mLocalOnlyHotspotRequests) {
if (mLocalOnlyHotspotRequests.remove(request.getPid()) == null) {
mLog.trace("LocalOnlyHotspotRequestInfo not found to remove").flush();
@@ -1143,14 +1174,14 @@ public class WifiServiceImpl extends BaseWifiService {
@GuardedBy("mLocalOnlyHotspotRequests")
private void stopIfEmptyLocked() {
if (mLocalOnlyHotspotRequests.isEmpty()) {
- mLocalOnlyHotspotConfig = null;
+ mActiveConfig = null;
+ mIsExclusive = false;
mLohsInterfaceName = null;
mLohsInterfaceMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED;
stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
}
}
-
/**
* Helper method to send a HOTSPOT_STARTED message to all registered LocalOnlyHotspotRequest
* callers.
@@ -1161,7 +1192,7 @@ public class WifiServiceImpl extends BaseWifiService {
private void sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked() {
for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) {
try {
- requestor.sendHotspotStartedMessage(mLocalOnlyHotspotConfig);
+ requestor.sendHotspotStartedMessage(mActiveConfig.getWifiConfiguration());
} catch (RemoteException e) {
// This will be cleaned up by binder death handling
}
@@ -1303,7 +1334,9 @@ public class WifiServiceImpl extends BaseWifiService {
* see {@link WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback)}
*
* @param callback Callback to communicate with WifiManager and allow cleanup if the app dies.
- * @param packageName String name of the calling package
+ * @param packageName String name of the calling package.
+ * @param customConfig Custom configuration to be applied to the hotspot, or null for a shared
+ * hotspot with framework-generated config.
*
* @return int return code for attempt to start LocalOnlyHotspot.
*
@@ -1313,7 +1346,8 @@ public class WifiServiceImpl extends BaseWifiService {
* have an outstanding request.
*/
@Override
- public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName) {
+ public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName,
+ SoftApConfiguration customConfig) {
// first check if the caller has permission to start a local only hotspot
// need to check for WIFI_STATE_CHANGE and location permission
final int uid = Binder.getCallingUid();
@@ -1321,13 +1355,21 @@ public class WifiServiceImpl extends BaseWifiService {
mLog.info("start uid=% pid=%").c(uid).c(pid).flush();
- if (enforceChangePermission(packageName) != MODE_ALLOWED) {
- return LocalOnlyHotspotCallback.ERROR_GENERIC;
- }
- enforceLocationPermission(packageName, uid);
- // also need to verify that Locations services are enabled.
- if (!Binder.withCleanCallingIdentity(() -> mWifiPermissionsUtil.isLocationModeEnabled())) {
- throw new SecurityException("Location mode is not enabled.");
+ // Permission requirements are different with/without custom config.
+ if (customConfig == null) {
+ if (enforceChangePermission(packageName) != MODE_ALLOWED) {
+ return LocalOnlyHotspotCallback.ERROR_GENERIC;
+ }
+ enforceLocationPermission(packageName, uid);
+ // also need to verify that Locations services are enabled.
+ if (!Binder.withCleanCallingIdentity(
+ () -> mWifiPermissionsUtil.isLocationModeEnabled())) {
+ throw new SecurityException("Location mode is not enabled.");
+ }
+ } else {
+ if (!isSettingsOrSuw(Binder.getCallingPid(), Binder.getCallingUid())) {
+ throw new SecurityException(TAG + ": Permission denied");
+ }
}
// verify that tethering is not disabled
@@ -1353,7 +1395,7 @@ public class WifiServiceImpl extends BaseWifiService {
// now create the new LOHS request info object
LocalOnlyHotspotRequestInfo request = new LocalOnlyHotspotRequestInfo(callback,
- new LocalOnlyRequestorCallback());
+ new LocalOnlyRequestorCallback(), customConfig);
return mLohsSoftApTracker.start(pid, request);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
index 4bfeac395..073ef4e3f 100644
--- a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
@@ -876,7 +876,7 @@ public class ActiveModeWardenTest extends WifiBaseTest {
SoftApModeConfiguration tetherConfig =
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
WifiConfiguration lohsConfigWC = WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext,
- WifiConfiguration.AP_BAND_2GHZ);
+ WifiConfiguration.AP_BAND_2GHZ, null);
SoftApModeConfiguration lohsConfig =
new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_LOCAL_ONLY, lohsConfigWC);
diff --git a/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java b/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java
index b90332ad2..31ede2fd0 100644
--- a/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import android.net.wifi.ILocalOnlyHotspotCallback;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.IBinder;
@@ -62,7 +63,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test
public void verifyBinderLinkToDeathIsCalled() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
verify(mAppBinder).linkToDeath(eq(mLOHSRequestInfo), eq(0));
}
@@ -71,7 +72,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test(expected = NullPointerException.class)
public void verifyNullCallbackChecked() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(null, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(null, mDeathCallback, null);
}
/**
@@ -79,7 +80,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test(expected = NullPointerException.class)
public void verifyNullDeathCallbackChecked() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, null);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, null, null);
}
/**
@@ -87,7 +88,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test
public void verifyUnlinkDeathRecipientUnlinksFromBinder() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
mLOHSRequestInfo.unlinkDeathRecipient();
verify(mAppBinder).unlinkToDeath(eq(mLOHSRequestInfo), eq(0));
}
@@ -97,7 +98,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test
public void verifyBinderDeathTriggersCallback() {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
mLOHSRequestInfo.binderDied();
verify(mDeathCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo));
}
@@ -109,7 +110,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
public void verifyRemoteExceptionTriggersCallback() throws Exception {
doThrow(mRemoteException).when(mAppBinder)
.linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
verify(mDeathCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo));
}
@@ -118,16 +119,25 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test
public void verifyPid() {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
assertEquals(Process.myPid(), mLOHSRequestInfo.getPid());
}
+ @Test
+ public void verifyCustomConfig() {
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .build();
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, config);
+ assertEquals(config, mLOHSRequestInfo.getCustomConfig());
+ }
+
/**
* Verify that sendHotspotFailedMessage does send a Message properly
*/
@Test
public void verifySendFailedMessage() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
mLOHSRequestInfo.sendHotspotFailedMessage(
WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC);
verify(mCallback).onHotspotFailed(WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC);
@@ -138,7 +148,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test
public void verifySendStartedMessage() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
WifiConfiguration config = mock(WifiConfiguration.class);
mLOHSRequestInfo.sendHotspotStartedMessage(config);
verify(mCallback).onHotspotStarted(config);
@@ -149,7 +159,7 @@ public class LocalOnlyHotspotRequestInfoTest extends WifiBaseTest {
*/
@Test
public void verifySendStoppedMessage() throws Exception {
- mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback);
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mCallback, mDeathCallback, null);
mLOHSRequestInfo.sendHotspotStoppedMessage();
verify(mCallback).onHotspotStopped();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index 1f5166abb..9fd36fbd9 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -22,6 +22,7 @@ import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
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;
@@ -37,7 +38,6 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -148,6 +148,8 @@ public class SoftApManagerTest extends WifiBaseTest {
TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
.thenReturn(true);
when(mWifiNative.getFactoryMacAddress(any())).thenReturn(TEST_MAC_ADDRESS);
+ when(mWifiApConfigStore.randomizeBssidIfUnset(any(), any())).thenAnswer(
+ (invocation) -> invocation.getArgument(1));
}
private WifiConfiguration createDefaultApConfig() {
@@ -1134,41 +1136,36 @@ public class SoftApManagerTest extends WifiBaseTest {
}
@Test
- public void setsRandomMacWhenEnabled() throws Exception {
+ public void resetsFactoryMacWhenRandomizationOff() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+ config.apBand = WifiConfiguration.AP_BAND_2GHZ;
+ config.SSID = TEST_SSID;
+ config.BSSID = null;
SoftApModeConfiguration apConfig =
- new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
- when(mResources.getBoolean(R.bool.config_wifi_ap_mac_randomization_supported))
- .thenReturn(true);
+ new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
ArgumentCaptor<MacAddress> mac = ArgumentCaptor.forClass(MacAddress.class);
+ when(mWifiNative.getFactoryMacAddress(TEST_INTERFACE_NAME)).thenReturn(TEST_MAC_ADDRESS);
when(mWifiNative.setMacAddress(eq(TEST_INTERFACE_NAME), mac.capture())).thenReturn(true);
startSoftApAndVerifyEnabled(apConfig);
- mSoftApManager.stop();
- mLooper.dispatchAll();
-
- clearInvocations(mWifiNative, mCallback, mSarManager, mWifiDiagnostics, mWifiMetrics,
- mListener, mFrameworkFacade, mContext);
-
- startSoftApAndVerifyEnabled(apConfig);
- mSoftApManager.stop();
- assertThat(mac.getAllValues()).hasSize(2);
- assertThat(mac.getAllValues()).containsNoDuplicates();
+ assertThat(mac.getValue()).isEqualTo(TEST_MAC_ADDRESS);
}
@Test
- public void resetsFactoryMacWhenRandomizationOff() throws Exception {
- when(mResources.getBoolean(R.bool.config_wifi_ap_mac_randomization_supported))
- .thenReturn(false);
- SoftApModeConfiguration apConfig =
- new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+ public void setsCustomMac() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+ config.apBand = WifiConfiguration.AP_BAND_2GHZ;
+ config.SSID = TEST_SSID;
+ config.BSSID = "23:34:45:56:67:78";
+ SoftApModeConfiguration apConfig = new SoftApModeConfiguration(
+ IFACE_IP_MODE_LOCAL_ONLY, config);
ArgumentCaptor<MacAddress> mac = ArgumentCaptor.forClass(MacAddress.class);
- when(mWifiNative.getFactoryMacAddress(TEST_INTERFACE_NAME)).thenReturn(TEST_MAC_ADDRESS);
when(mWifiNative.setMacAddress(eq(TEST_INTERFACE_NAME), mac.capture())).thenReturn(true);
startSoftApAndVerifyEnabled(apConfig);
- assertThat(mac.getValue()).isEqualTo(TEST_MAC_ADDRESS);
+ assertThat(mac.getValue()).isEqualTo(MacAddress.fromString("23:34:45:56:67:78"));
}
@Test
@@ -1238,7 +1235,7 @@ public class SoftApManagerTest extends WifiBaseTest {
WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
softApConfig.getTargetMode());
verify(mListener).onStarted();
- verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.mTargetMode);
+ verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.getTargetMode());
verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(true),
observerCaptor.capture());
mContentObserver = observerCaptor.getValue();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index 8799ba6e2..774af4de0 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -16,6 +16,8 @@
package com.android.server.wifi;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -31,6 +33,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.net.MacAddress;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.Build;
@@ -543,7 +547,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest {
@Test
public void generateLocalOnlyHotspotConfigIsValid() {
WifiConfiguration config = WifiApConfigStore
- .generateLocalOnlyHotspotConfig(mContext, WifiConfiguration.AP_BAND_2GHZ);
+ .generateLocalOnlyHotspotConfig(mContext, WifiConfiguration.AP_BAND_2GHZ, null);
verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID,
WifiConfiguration.AP_BAND_2GHZ);
// The LOHS config should also have a specific network id set - check that as well.
@@ -559,7 +563,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest {
@Test
public void generateLocalOnlyHotspotConfigIsValid5G() {
WifiConfiguration config = WifiApConfigStore
- .generateLocalOnlyHotspotConfig(mContext, WifiConfiguration.AP_BAND_5GHZ);
+ .generateLocalOnlyHotspotConfig(mContext, WifiConfiguration.AP_BAND_5GHZ, null);
verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID,
WifiConfiguration.AP_BAND_5GHZ);
// The LOHS config should also have a specific network id set - check that as well.
@@ -569,6 +573,59 @@ public class WifiApConfigStoreTest extends WifiBaseTest {
assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
}
+ @Test
+ public void generateLohsConfig_forwardsCustomMac() {
+ SoftApConfiguration customConfig = new SoftApConfiguration.Builder()
+ .setBssid(MacAddress.fromString("11:22:33:44:55:66"))
+ .build();
+ WifiConfiguration wifiConfig = WifiApConfigStore.generateLocalOnlyHotspotConfig(
+ mContext, WifiConfiguration.AP_BAND_2GHZ, customConfig);
+ assertThat(wifiConfig.BSSID).isNotEmpty();
+ assertThat(MacAddress.fromString(wifiConfig.BSSID)).isEqualTo(
+ MacAddress.fromString("11:22:33:44:55:66"));
+ }
+
+ @Test
+ public void randomizeBssid_randomizesWhenEnabled() {
+ mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, true);
+ WifiConfiguration baseConfig = new WifiConfiguration();
+
+ WifiApConfigStore store = createWifiApConfigStore();
+ WifiConfiguration config1 = store.randomizeBssidIfUnset(mContext, baseConfig);
+ WifiConfiguration config2 = store.randomizeBssidIfUnset(mContext, baseConfig);
+
+ assertThat(config1.BSSID).isNotNull();
+ assertThat(config2.BSSID).isNotNull();
+ MacAddress mac1 = MacAddress.fromString(config1.BSSID);
+ MacAddress mac2 = MacAddress.fromString(config2.BSSID);
+ assertThat(mac1).isNotEqualTo(mac2);
+ }
+
+ @Test
+ public void randomizeBssid_usesFactoryMacWhenRandomizationOff() {
+ mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, false);
+ WifiConfiguration baseConfig = new WifiConfiguration();
+
+ WifiApConfigStore store = createWifiApConfigStore();
+ WifiConfiguration config = store.randomizeBssidIfUnset(mContext, baseConfig);
+
+ assertThat(config.BSSID).isNull();
+ }
+
+ @Test
+ public void randomizeBssid_forwardsCustomMac() {
+ mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, true);
+ WifiConfiguration baseConfig = new WifiConfiguration();
+ baseConfig.BSSID = "11:22:33:44:55:66";
+
+ WifiApConfigStore store = createWifiApConfigStore();
+ WifiConfiguration config = store.randomizeBssidIfUnset(mContext, baseConfig);
+
+ assertThat(config.BSSID).isNotEmpty();
+ assertThat(MacAddress.fromString(config.BSSID)).isEqualTo(
+ MacAddress.fromString("11:22:33:44:55:66"));
+ }
+
/**
* Helper method to generate random SSIDs.
*
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index c531fd49e..0c85dc132 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -24,6 +24,7 @@ import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERI
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
@@ -35,6 +36,8 @@ import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -95,6 +98,7 @@ import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -145,7 +149,6 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -268,7 +271,7 @@ public class WifiServiceImplTest extends WifiBaseTest {
@Mock ILocalOnlyHotspotCallback mLohsCallback;
@Mock IScanResultsListener mClientScanResultsListener;
- @Spy FakeWifiLog mLog;
+ WifiLog mLog = new LogcatLog(TAG);
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -376,6 +379,10 @@ public class WifiServiceImplTest extends WifiBaseTest {
return null;
}
};
+
+ // permission not granted by default
+ doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.NETWORK_SETUP_WIZARD), any());
}
/**
@@ -1432,13 +1439,16 @@ public class WifiServiceImplTest extends WifiBaseTest {
assertTrue(retrievedScanResultList.isEmpty());
}
- private void registerLOHSRequestFull() {
- // allow test to proceed without a permission check failure
+ private void setupLohsPermissions() {
when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true);
when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true);
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING))
.thenReturn(false);
- int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME);
+ }
+
+ private void registerLOHSRequestFull() {
+ setupLohsPermissions();
+ int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME, null);
assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result);
verifyCheckChangePermission(TEST_PACKAGE_NAME);
}
@@ -1644,11 +1654,174 @@ public class WifiServiceImplTest extends WifiBaseTest {
private void verifyLohsBand(int expectedBand) {
verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture());
- final WifiConfiguration configuration = mSoftApModeConfigCaptor.getValue().mConfig;
+ final WifiConfiguration configuration =
+ mSoftApModeConfigCaptor.getValue().getWifiConfiguration();
assertNotNull(configuration);
assertEquals(expectedBand, configuration.apBand);
}
+ private static class FakeLohsCallback extends ILocalOnlyHotspotCallback.Stub {
+ boolean mIsStarted = false;
+ WifiConfiguration mWifiConfig = null;
+
+ @Override
+ public void onHotspotStarted(WifiConfiguration wifiConfig) {
+ mIsStarted = true;
+ this.mWifiConfig = wifiConfig;
+ }
+
+ @Override
+ public void onHotspotStopped() {
+ mIsStarted = false;
+ mWifiConfig = null;
+ }
+
+ @Override
+ public void onHotspotFailed(int i) {
+ mIsStarted = false;
+ mWifiConfig = null;
+ }
+ }
+
+ private void setupForCustomLohs() {
+ setupLohsPermissions();
+ when(mContext.checkPermission(eq(Manifest.permission.NETWORK_SETUP_WIZARD),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ setupWardenForCustomLohs();
+ }
+
+ private void setupWardenForCustomLohs() {
+ doAnswer(invocation -> {
+ changeLohsState(WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR);
+ mWifiServiceImpl.updateInterfaceIpState(mLohsInterfaceName, IFACE_IP_MODE_LOCAL_ONLY);
+ return null;
+ }).when(mActiveModeWarden).startSoftAp(any());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testCustomLohs_FailsWithoutPermission() {
+ SoftApConfiguration customConfig = new SoftApConfiguration.Builder()
+ .setSsid("customConfig")
+ .build();
+ // set up basic permissions, but not NETWORK_SETUP_WIZARD
+ setupLohsPermissions();
+ setupWardenForCustomLohs();
+ mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME, customConfig);
+ }
+
+ private static void nopDeathCallback(LocalOnlyHotspotRequestInfo requestor) {
+ }
+
+ @Test
+ public void testCustomLohs_ExclusiveAfterShared() {
+ FakeLohsCallback sharedCallback = new FakeLohsCallback();
+ FakeLohsCallback exclusiveCallback = new FakeLohsCallback();
+ SoftApConfiguration exclusiveConfig = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .build();
+
+ setupForCustomLohs();
+ mWifiServiceImpl.registerLOHSForTest(mPid, new LocalOnlyHotspotRequestInfo(
+ sharedCallback, WifiServiceImplTest::nopDeathCallback, null));
+ assertThat(mWifiServiceImpl.startLocalOnlyHotspot(
+ exclusiveCallback, TEST_PACKAGE_NAME, exclusiveConfig))
+ .isEqualTo(ERROR_GENERIC);
+ mLooper.dispatchAll();
+
+ assertThat(sharedCallback.mIsStarted).isTrue();
+ assertThat(exclusiveCallback.mIsStarted).isFalse();
+ }
+
+ @Test
+ public void testCustomLohs_ExclusiveBeforeShared() {
+ FakeLohsCallback sharedCallback = new FakeLohsCallback();
+ FakeLohsCallback exclusiveCallback = new FakeLohsCallback();
+ SoftApConfiguration exclusiveConfig = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .build();
+
+ setupForCustomLohs();
+ mWifiServiceImpl.registerLOHSForTest(mPid, new LocalOnlyHotspotRequestInfo(
+ exclusiveCallback, WifiServiceImplTest::nopDeathCallback, exclusiveConfig));
+ assertThat(mWifiServiceImpl.startLocalOnlyHotspot(
+ sharedCallback, TEST_PACKAGE_NAME, null))
+ .isEqualTo(ERROR_GENERIC);
+ mLooper.dispatchAll();
+
+ assertThat(exclusiveCallback.mIsStarted).isTrue();
+ assertThat(sharedCallback.mIsStarted).isFalse();
+ }
+
+ @Test
+ public void testCustomLohs_Wpa2() {
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .setWpa2Passphrase("passphrase")
+ .build();
+ FakeLohsCallback callback = new FakeLohsCallback();
+
+ setupForCustomLohs();
+ assertThat(mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, config))
+ .isEqualTo(REQUEST_REGISTERED);
+ mLooper.dispatchAll();
+
+ assertThat(callback.mIsStarted).isTrue();
+ assertThat(callback.mWifiConfig.SSID).isEqualTo("customSsid");
+ assertThat(callback.mWifiConfig.getAuthType()).isEqualTo(KeyMgmt.WPA2_PSK);
+ assertThat(callback.mWifiConfig.preSharedKey).isEqualTo("passphrase");
+ }
+
+ @Test
+ public void testCustomLohs_Open() {
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .build();
+ FakeLohsCallback callback = new FakeLohsCallback();
+
+ setupForCustomLohs();
+ assertThat(mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, config))
+ .isEqualTo(REQUEST_REGISTERED);
+ mLooper.dispatchAll();
+
+ assertThat(callback.mIsStarted).isTrue();
+ assertThat(callback.mWifiConfig.SSID).isEqualTo("customSsid");
+ assertThat(callback.mWifiConfig.getAuthType()).isEqualTo(KeyMgmt.NONE);
+ assertThat(callback.mWifiConfig.preSharedKey).isNull();
+ }
+
+ @Test
+ public void testCustomLohs_GeneratesSsidIfAbsent() {
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setWpa2Passphrase("passphrase")
+ .build();
+ FakeLohsCallback callback = new FakeLohsCallback();
+
+ setupForCustomLohs();
+ assertThat(mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, config))
+ .isEqualTo(REQUEST_REGISTERED);
+ mLooper.dispatchAll();
+
+ assertThat(callback.mIsStarted).isTrue();
+ assertThat(callback.mWifiConfig.SSID).isNotEmpty();
+ }
+
+ @Test
+ public void testCustomLohs_ForwardsBssid() {
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff"))
+ .build();
+ FakeLohsCallback callback = new FakeLohsCallback();
+
+ setupForCustomLohs();
+ assertThat(mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, config))
+ .isEqualTo(REQUEST_REGISTERED);
+ mLooper.dispatchAll();
+
+ assertThat(callback.mIsStarted).isTrue();
+ assertThat(callback.mWifiConfig.BSSID)
+ .ignoringCase().isEqualTo("aa:bb:cc:dd:ee:ff");
+ }
+
/**
* Verify that WifiServiceImpl does not send the stop ap message if there were no
* pending LOHS requests upon a binder death callback.