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