Home | History | Annotate | Download | only in wifi
      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 
     50     private WifiSsid() {
     51     }
     52 
     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 
     61     public static WifiSsid createFromAsciiEncoded(String asciiEncoded) {
     62         WifiSsid a = new WifiSsid();
     63         a.convertToBytes(asciiEncoded);
     64         return a;
     65     }
     66 
     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 */
     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
    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
    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
    206     public int hashCode() {
    207         return Arrays.hashCode(octets.toByteArray());
    208     }
    209 
    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 */
    218     public boolean isHidden() {
    219         return isArrayAllZeroes(octets.toByteArray());
    220     }
    221 
    222     /** @hide */
    223     public byte[] getOctets() {
    224         return octets.toByteArray();
    225     }
    226 
    227     /** @hide */
    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} */
    238     public int describeContents() {
    239         return 0;
    240     }
    241 
    242     /** Implement the Parcelable interface {@hide} */
    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