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