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