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