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