1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.libraries.tv.tvsystem.wifi; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.net.MacAddress; 23 import android.net.wifi.WifiConfiguration; 24 import android.net.wifi.WifiManager; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import com.android.internal.util.Preconditions; 29 30 import java.nio.charset.StandardCharsets; 31 import java.util.Objects; 32 import java.util.concurrent.Executor; 33 34 /** 35 * WiFi configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot). 36 * 37 * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the 38 * framework how it should open a hotspot. It is not meant to describe the network as it will be 39 * seen by clients; this role is currently served by {@link WifiConfiguration} (see 40 * {@link WifiManager.LocalOnlyHotspotReservation#getWifiConfiguration()}). 41 * 42 * Apps can use this to configure a local-only hotspot using 43 * {@link TvWifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor, 44 * WifiManager.LocalOnlyHotspotCallback)}. 45 * 46 * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to 47 * create a new instance. 48 * 49 * @hide 50 */ 51 @SystemApi 52 public final class SoftApConfiguration implements Parcelable { 53 /** 54 * SSID for the AP, or null for a framework-determined SSID. 55 */ 56 private final @Nullable 57 String mSsid; 58 /** 59 * BSSID for the AP, or null to use a framework-determined BSSID. 60 */ 61 private final @Nullable 62 MacAddress mBssid; 63 /** 64 * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK). 65 */ 66 private final @Nullable 67 String mWpa2Passphrase; 68 69 /** Private constructor for Builder and Parcelable implementation. */ SoftApConfiguration( @ullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase)70 private SoftApConfiguration( 71 @Nullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase) { 72 mSsid = ssid; 73 mBssid = bssid; 74 mWpa2Passphrase = wpa2Passphrase; 75 } 76 77 @Override equals(Object otherObj)78 public boolean equals(Object otherObj) { 79 if (this == otherObj) { 80 return true; 81 } 82 if (!(otherObj instanceof SoftApConfiguration)) { 83 return false; 84 } 85 SoftApConfiguration other = (SoftApConfiguration) otherObj; 86 return Objects.equals(mSsid, other.mSsid) 87 && Objects.equals(mBssid, other.mBssid) 88 && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase); 89 } 90 91 @Override hashCode()92 public int hashCode() { 93 return Objects.hash(mSsid, mBssid, mWpa2Passphrase); 94 } 95 96 @Override writeToParcel(@onNull Parcel dest, int flags)97 public void writeToParcel(@NonNull Parcel dest, int flags) { 98 dest.writeString(mSsid); 99 dest.writeParcelable(mBssid, flags); 100 dest.writeString(mWpa2Passphrase); 101 } 102 103 @Override describeContents()104 public int describeContents() { 105 return 0; 106 } 107 108 @NonNull 109 public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() { 110 @Override 111 public SoftApConfiguration createFromParcel(Parcel in) { 112 return new SoftApConfiguration( 113 in.readString(), 114 in.readParcelable(MacAddress.class.getClassLoader()), 115 in.readString()); 116 } 117 118 @Override 119 public SoftApConfiguration[] newArray(int size) { 120 return new SoftApConfiguration[size]; 121 } 122 }; 123 124 @Nullable getSsid()125 public String getSsid() { 126 return mSsid; 127 } 128 129 @Nullable getBssid()130 public MacAddress getBssid() { 131 return mBssid; 132 } 133 134 @Nullable getWpa2Passphrase()135 public String getWpa2Passphrase() { 136 return mWpa2Passphrase; 137 } 138 139 /** 140 * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a 141 * Soft AP. 142 * 143 * All fields are optional. By default, SSID and BSSID are automatically chosen by the 144 * framework, and an open network is created. 145 */ 146 public static final class Builder { 147 private String mSsid; 148 private MacAddress mBssid; 149 private String mWpa2Passphrase; 150 151 /** 152 * Constructs a Builder with default values (see {@link Builder}). 153 */ Builder()154 public Builder() { 155 mSsid = null; 156 mBssid = null; 157 mWpa2Passphrase = null; 158 } 159 160 /** 161 * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance. 162 */ Builder(@onNull SoftApConfiguration other)163 public Builder(@NonNull SoftApConfiguration other) { 164 Objects.requireNonNull(other); 165 166 mSsid = other.mSsid; 167 mBssid = other.mBssid; 168 mWpa2Passphrase = other.mWpa2Passphrase; 169 } 170 171 /** 172 * Builds the {@link SoftApConfiguration}. 173 * 174 * @return A new {@link SoftApConfiguration}, as configured by previous method calls. 175 */ 176 @NonNull build()177 public SoftApConfiguration build() { 178 return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase); 179 } 180 181 /** 182 * Specifies an SSID for the AP. 183 * 184 * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically 185 * chosen by the framework. 186 * @return Builder for chaining. 187 * @throws IllegalArgumentException when the SSID is empty or not valid Unicode. 188 */ 189 @NonNull setSsid(@ullable String ssid)190 public Builder setSsid(@Nullable String ssid) { 191 if (ssid != null) { 192 Preconditions.checkStringNotEmpty(ssid); 193 Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid)); 194 } 195 mSsid = ssid; 196 return this; 197 } 198 199 /** 200 * Specifies a BSSID for the AP. 201 * 202 * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is 203 * responsible for avoiding collisions. 204 * @return Builder for chaining. 205 * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC 206 * address. 207 */ 208 @NonNull setBssid(@ullable MacAddress bssid)209 public Builder setBssid(@Nullable MacAddress bssid) { 210 if (bssid != null) { 211 Preconditions.checkArgument(!bssid.equals( 212 MacAddress.fromString("00:00:00:00:00:00"))); 213 Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS)); 214 } 215 mBssid = bssid; 216 return this; 217 } 218 219 /** 220 * Specifies that this AP should use WPA2-PSK with the given passphrase. When set to null 221 * and no other encryption method is configured, an open network is created. 222 * 223 * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK 224 * configuration. 225 * @return Builder for chaining. 226 * @throws IllegalArgumentException when the passphrase is the empty string 227 */ 228 @NonNull setWpa2Passphrase(@ullable String passphrase)229 public Builder setWpa2Passphrase(@Nullable String passphrase) { 230 if (passphrase != null) { 231 Preconditions.checkStringNotEmpty(passphrase); 232 } 233 mWpa2Passphrase = passphrase; 234 return this; 235 } 236 } 237 } 238