/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi; import static android.net.wifi.ScanResult.UNSPECIFIED; import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.MacAddress; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.wifi.ScanResult.WifiBand; import android.os.Parcel; import android.os.Parcelable; import android.os.PatternMatcher; import android.text.TextUtils; import android.util.Pair; import com.android.modules.utils.build.SdkLevel; import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; /** * Network specifier object used to request a Wi-Fi network. Apps should use the * {@link WifiNetworkSpecifier.Builder} class to create an instance. *
* This specifier can be used to request a local-only connection on devices that support concurrent * connections (indicated via * {@link WifiManager#isStaConcurrencyForLocalOnlyConnectionsSupported()} and if the initiating app * targets SDK ≥ {@link android.os.Build.VERSION_CODES#S} or is a system app. These local-only * connections may be brought up as a secondary concurrent connection (primary connection will be * used for networks with internet connectivity available to the user and all apps). *
** This specifier can also be used to listen for connected Wi-Fi networks on a particular band. * Additionally, some devices may support requesting a connection to a particular band. If the * device does not support such a request, it will send {@link NetworkCallback#onUnavailable()} * upon request to the callback passed to * {@link ConnectivityManager#requestNetwork(NetworkRequest, NetworkCallback)} or equivalent. * See {@link Builder#build()} for details. *
*/ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parcelable { private static final String TAG = "WifiNetworkSpecifier"; /** * Returns the band for a given frequency in MHz. * @hide */ @WifiBand public static int getBand(final int freqMHz) { if (ScanResult.is24GHz(freqMHz)) { return ScanResult.WIFI_BAND_24_GHZ; } else if (ScanResult.is5GHz(freqMHz)) { return ScanResult.WIFI_BAND_5_GHZ; } else if (ScanResult.is6GHz(freqMHz)) { return ScanResult.WIFI_BAND_6_GHZ; } else if (ScanResult.is60GHz(freqMHz)) { return ScanResult.WIFI_BAND_60_GHZ; } return UNSPECIFIED; } /** * Check the channel in the array is valid. * @hide */ public static boolean validateChannelFrequencyInMhz(@NonNull int[] channels) { if (channels == null) { return false; } for (int channel : channels) { if (ScanResult.convertFrequencyMhzToChannelIfSupported(channel) == UNSPECIFIED) { return false; } } return true; } /** * Validates that the passed band is a valid band * @param band the band to check * @return true if the band is valid, false otherwise * @hide */ public static boolean validateBand(@WifiBand int band) { switch (band) { case UNSPECIFIED: case ScanResult.WIFI_BAND_24_GHZ: case ScanResult.WIFI_BAND_5_GHZ: case ScanResult.WIFI_BAND_5_GHZ_LOW: case ScanResult.WIFI_BAND_5_GHZ_HIGH: case ScanResult.WIFI_BAND_6_GHZ: case ScanResult.WIFI_BAND_60_GHZ: return true; default: return false; } } /** * Builder used to create {@link WifiNetworkSpecifier} objects. */ public static final class Builder { private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*"; private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; private static final Pair*
*
*
*
*
*
* When using with {@link ConnectivityManager#requestNetwork(NetworkRequest, * NetworkCallback)} or variants, note that some devices may not support requesting a * network with all combinations of specifier members. For example, some devices may only * support requesting local-only networks (networks without the * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability), or not support * requesting a particular band. However, there are no restrictions when using * {@link ConnectivityManager#registerNetworkCallback(NetworkRequest, NetworkCallback)} * or other similar methods which monitor but do not request networks. * * If the device can't support a request, the app will receive a call to * {@link NetworkCallback#onUnavailable()}. *
* ** When requesting a local-only network, apps can set a combination of network match params: *
{@code * final NetworkSpecifier specifier = * new Builder() * .setSsidPattern(new PatternMatcher("test", PatternMatcher.PATTERN_PREFIX)) * .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"), * MacAddress.fromString("ff:ff:ff:00:00:00")) * .build() * final NetworkRequest request = * new NetworkRequest.Builder() * .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) * .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) * .setNetworkSpecifier(specifier) * .build(); * final ConnectivityManager connectivityManager = * context.getSystemService(Context.CONNECTIVITY_SERVICE); * final NetworkCallback networkCallback = new NetworkCallback() { * ... * {@literal @}Override * void onAvailable(...) {} * // etc. * }; * connectivityManager.requestNetwork(request, networkCallback); * }* * @return Instance of {@link NetworkSpecifier}. * @throws IllegalStateException on invalid params set. */ public @NonNull WifiNetworkSpecifier build() { if (!hasSetAnyPattern() && mBand == UNSPECIFIED) { throw new IllegalStateException("one of setSsidPattern/setSsid/setBssidPattern/" + "setBssid/setBand should be invoked for specifier"); } setMatchAnyPatternIfUnset(); if (hasSetMatchNonePattern()) { throw new IllegalStateException("cannot set match-none pattern for specifier"); } if (hasSetMatchAllPattern() && mBand == UNSPECIFIED) { throw new IllegalStateException("cannot set match-all pattern for specifier"); } if (mIsHiddenSSID && mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL) { throw new IllegalStateException("setSsid should also be invoked when " + "setIsHiddenSsid is invoked for network specifier"); } if (mChannels.length != 0 && mBand != UNSPECIFIED) { throw new IllegalStateException("cannot setPreferredChannelsFrequencyInMhz with " + "setBand together"); } validateSecurityParams(); return new WifiNetworkSpecifier( mSsidPatternMatcher, mBssidPatternMatcher, mBand, buildWifiConfiguration(), mChannels); } } /** * SSID pattern match specified by the app. * @hide */ public final PatternMatcher ssidPatternMatcher; /** * BSSID pattern match specified by the app. * Pair of
* Note: {@link WifiConfiguration#SSID} & {@link WifiConfiguration#BSSID} fields from
* WifiConfiguration are not used. Instead we use the {@link #ssidPatternMatcher} &
* {@link #bssidPatternMatcher} fields embedded directly
* within {@link WifiNetworkSpecifier}.
* @hide
*/
public final WifiConfiguration wifiConfiguration;
/** @hide */
public WifiNetworkSpecifier() throws IllegalAccessException {
throw new IllegalAccessException("Use the builder to create an instance");
}
/** @hide */
public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher,
@NonNull Pair