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