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.os.Parcel; 20 import android.os.Parcelable; 21 22 import java.io.ByteArrayOutputStream; 23 import java.nio.ByteBuffer; 24 import java.nio.CharBuffer; 25 import java.nio.charset.Charset; 26 import java.nio.charset.CharsetDecoder; 27 import java.nio.charset.CoderResult; 28 import java.nio.charset.CodingErrorAction; 29 import java.util.Arrays; 30 import java.util.Locale; 31 32 /** 33 * Stores SSID octets and handles conversion. 34 * 35 * For Ascii encoded string, any octet < 32 or > 127 is encoded as 36 * a "\x" followed by the hex representation of the octet. 37 * Exception chars are ", \, \e, \n, \r, \t which are escaped by a \ 38 * See src/utils/common.c for the implementation in the supplicant. 39 * 40 * @hide 41 */ 42 public class WifiSsid implements Parcelable { 43 private static final String TAG = "WifiSsid"; 44 45 public final ByteArrayOutputStream octets = new ByteArrayOutputStream(32); 46 47 private static final int HEX_RADIX = 16; 48 public static final String NONE = "<unknown ssid>"; 49 WifiSsid()50 private WifiSsid() { 51 } 52 createFromByteArray(byte ssid[])53 public static WifiSsid createFromByteArray(byte ssid[]) { 54 WifiSsid wifiSsid = new WifiSsid(); 55 if (ssid != null) { 56 wifiSsid.octets.write(ssid, 0/* the start offset */, ssid.length);; 57 } 58 return wifiSsid; 59 } 60 createFromAsciiEncoded(String asciiEncoded)61 public static WifiSsid createFromAsciiEncoded(String asciiEncoded) { 62 WifiSsid a = new WifiSsid(); 63 a.convertToBytes(asciiEncoded); 64 return a; 65 } 66 createFromHex(String hexStr)67 public static WifiSsid createFromHex(String hexStr) { 68 WifiSsid a = new WifiSsid(); 69 if (hexStr == null) return a; 70 71 if (hexStr.startsWith("0x") || hexStr.startsWith("0X")) { 72 hexStr = hexStr.substring(2); 73 } 74 75 for (int i = 0; i < hexStr.length()-1; i += 2) { 76 int val; 77 try { 78 val = Integer.parseInt(hexStr.substring(i, i + 2), HEX_RADIX); 79 } catch(NumberFormatException e) { 80 val = 0; 81 } 82 a.octets.write(val); 83 } 84 return a; 85 } 86 87 /* This function is equivalent to printf_decode() at src/utils/common.c in 88 * the supplicant */ convertToBytes(String asciiEncoded)89 private void convertToBytes(String asciiEncoded) { 90 int i = 0; 91 int val = 0; 92 while (i< asciiEncoded.length()) { 93 char c = asciiEncoded.charAt(i); 94 switch (c) { 95 case '\\': 96 i++; 97 switch(asciiEncoded.charAt(i)) { 98 case '\\': 99 octets.write('\\'); 100 i++; 101 break; 102 case '"': 103 octets.write('"'); 104 i++; 105 break; 106 case 'n': 107 octets.write('\n'); 108 i++; 109 break; 110 case 'r': 111 octets.write('\r'); 112 i++; 113 break; 114 case 't': 115 octets.write('\t'); 116 i++; 117 break; 118 case 'e': 119 octets.write(27); //escape char 120 i++; 121 break; 122 case 'x': 123 i++; 124 try { 125 val = Integer.parseInt(asciiEncoded.substring(i, i + 2), HEX_RADIX); 126 } catch (NumberFormatException e) { 127 val = -1; 128 } 129 if (val < 0) { 130 val = Character.digit(asciiEncoded.charAt(i), HEX_RADIX); 131 if (val < 0) break; 132 octets.write(val); 133 i++; 134 } else { 135 octets.write(val); 136 i += 2; 137 } 138 break; 139 case '0': 140 case '1': 141 case '2': 142 case '3': 143 case '4': 144 case '5': 145 case '6': 146 case '7': 147 val = asciiEncoded.charAt(i) - '0'; 148 i++; 149 if (asciiEncoded.charAt(i) >= '0' && asciiEncoded.charAt(i) <= '7') { 150 val = val * 8 + asciiEncoded.charAt(i) - '0'; 151 i++; 152 } 153 if (asciiEncoded.charAt(i) >= '0' && asciiEncoded.charAt(i) <= '7') { 154 val = val * 8 + asciiEncoded.charAt(i) - '0'; 155 i++; 156 } 157 octets.write(val); 158 break; 159 default: 160 break; 161 } 162 break; 163 default: 164 octets.write(c); 165 i++; 166 break; 167 } 168 } 169 } 170 171 @Override toString()172 public String toString() { 173 byte[] ssidBytes = octets.toByteArray(); 174 // Supplicant returns \x00\x00\x00\x00\x00\x00\x00\x00 hex string 175 // for a hidden access point. Make sure we maintain the previous 176 // behavior of returning empty string for this case. 177 if (octets.size() <= 0 || isArrayAllZeroes(ssidBytes)) return ""; 178 // TODO: Handle conversion to other charsets upon failure 179 Charset charset = Charset.forName("UTF-8"); 180 CharsetDecoder decoder = charset.newDecoder() 181 .onMalformedInput(CodingErrorAction.REPLACE) 182 .onUnmappableCharacter(CodingErrorAction.REPLACE); 183 CharBuffer out = CharBuffer.allocate(32); 184 185 CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true); 186 out.flip(); 187 if (result.isError()) { 188 return NONE; 189 } 190 return out.toString(); 191 } 192 193 @Override equals(Object thatObject)194 public boolean equals(Object thatObject) { 195 if (this == thatObject) { 196 return true; 197 } 198 if (!(thatObject instanceof WifiSsid)) { 199 return false; 200 } 201 WifiSsid that = (WifiSsid) thatObject; 202 return Arrays.equals(octets.toByteArray(), that.octets.toByteArray()); 203 } 204 205 @Override hashCode()206 public int hashCode() { 207 return Arrays.hashCode(octets.toByteArray()); 208 } 209 isArrayAllZeroes(byte[] ssidBytes)210 private boolean isArrayAllZeroes(byte[] ssidBytes) { 211 for (int i = 0; i< ssidBytes.length; i++) { 212 if (ssidBytes[i] != 0) return false; 213 } 214 return true; 215 } 216 217 /** @hide */ isHidden()218 public boolean isHidden() { 219 return isArrayAllZeroes(octets.toByteArray()); 220 } 221 222 /** @hide */ getOctets()223 public byte[] getOctets() { 224 return octets.toByteArray(); 225 } 226 227 /** @hide */ getHexString()228 public String getHexString() { 229 String out = "0x"; 230 byte[] ssidbytes = getOctets(); 231 for (int i = 0; i < octets.size(); i++) { 232 out += String.format(Locale.US, "%02x", ssidbytes[i]); 233 } 234 return (octets.size() > 0) ? out : null; 235 } 236 237 /** Implement the Parcelable interface {@hide} */ describeContents()238 public int describeContents() { 239 return 0; 240 } 241 242 /** Implement the Parcelable interface {@hide} */ writeToParcel(Parcel dest, int flags)243 public void writeToParcel(Parcel dest, int flags) { 244 dest.writeInt(octets.size()); 245 dest.writeByteArray(octets.toByteArray()); 246 } 247 248 /** Implement the Parcelable interface {@hide} */ 249 public static final Creator<WifiSsid> CREATOR = 250 new Creator<WifiSsid>() { 251 public WifiSsid createFromParcel(Parcel in) { 252 WifiSsid ssid = new WifiSsid(); 253 int length = in.readInt(); 254 byte b[] = new byte[length]; 255 in.readByteArray(b); 256 ssid.octets.write(b, 0, length); 257 return ssid; 258 } 259 260 public WifiSsid[] newArray(int size) { 261 return new WifiSsid[size]; 262 } 263 }; 264 } 265