diff options
author | Patrik Fimml <patrikf@google.com> | 2019-10-30 14:03:18 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-10-30 14:03:18 +0000 |
commit | 2124587f8af4ca1f9f2787747dd65d7eb9db9708 (patch) | |
tree | c76172c6292f4e9ef387c82d16068d3d9bed9c08 | |
parent | 39bb4b840175768cb6756a8ed8a76c48663cf897 (diff) | |
parent | b062c908670fb4180f6935d0cac0927d11fe7b6b (diff) |
Merge "Configurable SoftAP: Service implementation."
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. |