• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 android.net.wifi;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.net.wifi.util.HexEncoding;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.text.TextUtils;
26 
27 import java.io.ByteArrayOutputStream;
28 import java.nio.ByteBuffer;
29 import java.nio.CharBuffer;
30 import java.nio.charset.Charset;
31 import java.nio.charset.CharsetDecoder;
32 import java.nio.charset.CoderResult;
33 import java.nio.charset.CodingErrorAction;
34 import java.nio.charset.StandardCharsets;
35 import java.util.Arrays;
36 
37 /**
38  * Representation of a Wi-Fi Service Set Identifier (SSID).
39  */
40 public final class WifiSsid implements Parcelable {
41     private final byte[] mBytes;
42 
43     /**
44      * Creates a WifiSsid from the raw bytes. If the byte array is null, creates an empty WifiSsid
45      * object which will return an empty byte array and empty text.
46      * @param bytes the SSID
47      * @throws IllegalArgumentException if the raw byte array is longer than 32 bytes.
48      */
WifiSsid(@ullable byte[] bytes)49     private WifiSsid(@Nullable byte[] bytes) {
50         if (bytes == null) {
51             bytes = new byte[0];
52         }
53         if (bytes.length > 32) {
54             throw new IllegalArgumentException(
55                     "Max SSID length is 32 bytes, but received " + bytes.length + " bytes!");
56         }
57         mBytes = bytes;
58         // Duplicate the bytes to #octets for legacy apps.
59         octets.write(bytes, 0, bytes.length);
60     }
61 
62     /**
63      * Create a WifiSsid from the raw bytes. If the byte array is null, return an empty WifiSsid
64      * object which will return an empty byte array and empty text.
65      * @throws IllegalArgumentException if the raw byte array is longer than 32 bytes.
66      */
67     @NonNull
fromBytes(@ullable byte[] bytes)68     public static WifiSsid fromBytes(@Nullable byte[] bytes) {
69         return new WifiSsid(bytes);
70     }
71 
72     /**
73      * Returns the raw byte array representing this SSID.
74      * @return the SSID
75      */
76     @NonNull
getBytes()77     public byte[] getBytes() {
78         return mBytes;
79     }
80 
81     /**
82      * Create a UTF-8 WifiSsid from unquoted plaintext. If the text is null, return an
83      * empty WifiSsid object which will return an empty byte array and empty text.
84      * @throws IllegalArgumentException if the encoded UTF-8 byte array is longer than 32 bytes.
85      * @hide
86      */
87     @NonNull
fromUtf8Text(@ullable CharSequence utf8Text)88     public static WifiSsid fromUtf8Text(@Nullable CharSequence utf8Text) {
89         if (utf8Text == null) {
90             return new WifiSsid(null);
91         }
92         return new WifiSsid(utf8Text.toString().getBytes(StandardCharsets.UTF_8));
93     }
94 
95     /**
96      * If the SSID is encoded with UTF-8, this method returns the decoded SSID as plaintext.
97      * Otherwise, it returns {@code null}.
98      * @return the SSID
99      * @hide
100      */
101     @Nullable
getUtf8Text()102     public CharSequence getUtf8Text() {
103         return decodeSsid(mBytes, StandardCharsets.UTF_8);
104     }
105 
106     /**
107      * Create a WifiSsid from a string matching the format of {@link WifiSsid#toString()}.
108      * If the string is null, return an empty WifiSsid object which will return an empty byte array
109      * and empty text.
110      * @throws IllegalArgumentException if the string is unquoted but not hexadecimal,
111      *                                  if the hexadecimal string is odd-length,
112      *                                  or if the encoded byte array is longer than 32 bytes.
113      * @hide
114      */
115     @NonNull
fromString(@ullable String string)116     public static WifiSsid fromString(@Nullable String string) {
117         if (string == null) {
118             return new WifiSsid(null);
119         }
120         final int length = string.length();
121         if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
122             return new WifiSsid(string.substring(1, length - 1).getBytes(StandardCharsets.UTF_8));
123         }
124         return new WifiSsid(HexEncoding.decode(string));
125     }
126 
127     /**
128      * Returns the string representation of the WifiSsid. If the SSID can be decoded as UTF-8, it
129      * will be returned in plain text surrounded by double quotation marks. Otherwise, it is
130      * returned as an unquoted string of hex digits. This format is consistent with
131      * {@link WifiInfo#getSSID()} and {@link WifiConfiguration#SSID}.
132      *
133      * @return SSID as double-quoted plain text from UTF-8 or unquoted hex digits
134      */
135     @Override
136     @NonNull
toString()137     public String toString() {
138         String utf8String = decodeSsid(mBytes, StandardCharsets.UTF_8);
139         if (TextUtils.isEmpty(utf8String)) {
140             return HexEncoding.encodeToString(mBytes);
141         }
142         return "\"" + utf8String + "\"";
143     }
144 
145     /**
146      * Returns the given SSID bytes as a String decoded using the given Charset. If the bytes cannot
147      * be decoded, then this returns {@code null}.
148      * @param ssidBytes SSID as bytes
149      * @param charset Charset to decode with
150      * @return SSID as string, or {@code null}.
151      */
152     @Nullable
decodeSsid(@onNull byte[] ssidBytes, @NonNull Charset charset)153     private static String decodeSsid(@NonNull byte[] ssidBytes, @NonNull Charset charset) {
154         CharsetDecoder decoder = charset.newDecoder()
155                 .onMalformedInput(CodingErrorAction.REPORT)
156                 .onUnmappableCharacter(CodingErrorAction.REPORT);
157         CharBuffer out = CharBuffer.allocate(32);
158         CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true);
159         out.flip();
160         if (result.isError()) {
161             return null;
162         }
163         return out.toString();
164     }
165 
166     @Override
equals(Object thatObject)167     public boolean equals(Object thatObject) {
168         if (this == thatObject) {
169             return true;
170         }
171         if (!(thatObject instanceof WifiSsid)) {
172             return false;
173         }
174         WifiSsid that = (WifiSsid) thatObject;
175         return Arrays.equals(mBytes, that.mBytes);
176     }
177 
178     @Override
hashCode()179     public int hashCode() {
180         return Arrays.hashCode(mBytes);
181     }
182 
183     /** Implement the Parcelable interface */
184     @Override
describeContents()185     public int describeContents() {
186         return 0;
187     }
188 
189     /** Implement the Parcelable interface */
190     @Override
writeToParcel(@onNull Parcel dest, int flags)191     public void writeToParcel(@NonNull Parcel dest, int flags) {
192         dest.writeByteArray(mBytes);
193     }
194 
195     /** Implement the Parcelable interface */
196     public static final @NonNull Creator<WifiSsid> CREATOR =
197             new Creator<WifiSsid>() {
198                 @Override
199                 public WifiSsid createFromParcel(Parcel in) {
200                     return new WifiSsid(in.createByteArray());
201                 }
202 
203                 @Override
204                 public WifiSsid[] newArray(int size) {
205                     return new WifiSsid[size];
206                 }
207             };
208 
209     /**
210      * Use {@link #getBytes()} instead.
211      * @hide
212      */
213     // TODO(b/231433398): add maxTargetSdk = Build.VERSION_CODES.S
214     @UnsupportedAppUsage(publicAlternatives = "{@link #getBytes()}")
215     public final ByteArrayOutputStream octets = new ByteArrayOutputStream(32);
216 
217     /**
218      * Use {@link android.net.wifi.WifiManager#UNKNOWN_SSID} instead.
219      * @hide
220      */
221     // TODO(b/231433398): add maxTargetSdk = Build.VERSION_CODES.S
222     @UnsupportedAppUsage(publicAlternatives = "{@link android.net.wifi.WifiManager#UNKNOWN_SSID}")
223     public static final String NONE = WifiManager.UNKNOWN_SSID;
224 
225     /**
226      * Use {@link #fromBytes(byte[])} instead.
227      * @hide
228      */
229     // TODO(b/231433398): add maxTargetSdk = Build.VERSION_CODES.S
230     @UnsupportedAppUsage(publicAlternatives = "{@link #fromBytes(byte[])}")
createFromAsciiEncoded(String asciiEncoded)231     public static WifiSsid createFromAsciiEncoded(String asciiEncoded) {
232         return fromUtf8Text(asciiEncoded);
233     }
234 
235     /**
236      * Use {@link #getBytes()} instead.
237      * @hide
238      */
239     // TODO(b/231433398): add maxTargetSdk = Build.VERSION_CODES.S
240     @UnsupportedAppUsage(publicAlternatives = "{@link #getBytes()}")
getOctets()241     public byte[] getOctets() {
242         return getBytes();
243     }
244 }
245