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.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