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