| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi; |
| |
| import android.os.Parcelable; |
| import android.os.Parcel; |
| |
| import java.util.BitSet; |
| |
| import java.nio.ByteBuffer; |
| |
| import java.util.Date; |
| |
| /** |
| * Describes information about a detected access point. In addition |
| * to the attributes described here, the supplicant keeps track of |
| * {@code quality}, {@code noise}, and {@code maxbitrate} attributes, |
| * but does not currently report them to external clients. |
| */ |
| public class WifiParser { |
| |
| public WifiParser() {} |
| |
| |
| /* |
| * {@hide} |
| */ |
| class IE { |
| int id; |
| byte data[]; |
| } |
| |
| private static final int VENDOR_SPECIFIC_IE = 221; |
| private static final int IEEE_RSN_IE = 48; //IEEE 2012 8.4.2.27 |
| |
| |
| private static final int WPA_IE_VENDOR_TYPE = 0x0050f201; //WFA WPA vendor IE OUI/type |
| |
| /* |
| * parse beacon or probe response frame and build the capabilities |
| * {@hide} |
| * |
| * This function is called so as to build the capabilities string of the scan result, hence it is called |
| * by AutoJoin controller when handling scan results that are coming from WifiScanner. |
| * |
| * It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, but build the |
| * ScanResult.capabilities String in a way that mirror the values returned by wpa_supplicant. |
| * |
| * Once the capabilities string is build, the ScanResult can be used be the system as if it was coming from supplicant. |
| */ |
| /* @hide |
| * */ |
| static public String parse_akm(IE full_IE[], BitSet ieee_cap) { |
| boolean privacy = false; |
| boolean error = false; |
| if (ieee_cap == null) |
| return null; |
| |
| if (full_IE == null) |
| return null; |
| |
| privacy = ieee_cap.get(4); |
| |
| String capabilities = ""; |
| boolean rsne_found = false; |
| boolean wpa_found = false; |
| |
| for (IE ie : full_IE) { |
| String security = ""; |
| if (ie.id == IEEE_RSN_IE) { |
| rsne_found = true; |
| //parsing WPA2 capabilities |
| |
| ByteBuffer buf = ByteBuffer.wrap(ie.data); |
| |
| int total_len = ie.data.length; |
| int offset = 2; |
| |
| //version |
| if ((total_len - offset) < 2) { |
| //not enough space for version field |
| security = ""; |
| error = true; |
| break; |
| } |
| int val = 0; |
| if (0x0100 != buf.getShort(offset)) { |
| //incorrect version |
| security = ""; |
| error = true; |
| break; |
| } |
| offset += 2; |
| |
| |
| //group cipher |
| if ((total_len - offset) < 4) { |
| security = ""; //parse error on group cipher suite |
| error = true; |
| break; |
| } |
| offset += 4; //skip the group cipher |
| |
| security = "[WPA2"; //found the RSNE IE, hence start building the capability string |
| |
| //pairwise cipher |
| if ((total_len - offset) < 2) { |
| security = ""; //parse error no pairwise cipher |
| error = true; |
| break; |
| } |
| val = buf.getShort(offset); |
| if ((total_len - offset) < (2 + val * 4)) { |
| security = ""; //parse error no pairwise cipher |
| error = true; |
| break; |
| } |
| offset += 2 + val * 4; //skip the pairwise ciphers |
| |
| //AKM |
| if ((total_len - offset) < 2) { |
| security = ""; //parse error no AKM |
| error = true; |
| break; |
| } |
| val = buf.getShort(offset); |
| if ((total_len - offset) < (2 + val * 4)) { |
| security = ""; //parse error no pairwise cipher |
| error = true; |
| break; |
| } |
| offset += 2; |
| if (val == 0) { |
| security += "-EAP"; //default AKM |
| } |
| for (int i = 0; i < val; i++) { |
| int akm = buf.getInt(offset); |
| boolean found = false; |
| switch (akm) { |
| case 0x01ac0f00: |
| security += found ? "+" : "-" + "EAP"; |
| found = true; |
| break; |
| case 0x02ac0f00: |
| security += found ? "+" : "-" + "PSK"; //PSK as 802.11-2012 11.6.1.2 |
| found = true; |
| break; |
| case 0x03ac0f00: |
| security += found ? "+" : "-" + "FT/EAP"; |
| found = true; |
| break; |
| case 0x04ac0f00: |
| security += found ? "+" : "-" + "FT/PSK"; |
| found = true; |
| break; |
| case 0x06ac0f00: |
| security += found ? "+" : "-" + "PSK-SHA256"; |
| found = true; |
| break; |
| case 0x05ac0f00: |
| security += found ? "+" : "-" + "EAP-SHA256"; |
| found = true; |
| break; |
| } |
| offset += 4; |
| } |
| //we parsed what we want at this point |
| security += "]"; |
| capabilities += security; |
| |
| } |
| |
| if (ie.id == VENDOR_SPECIFIC_IE) { |
| int total_len = ie.data.length; |
| int offset = 2; |
| |
| //version |
| if ((total_len - offset) < 4) { |
| //not enough space for OUI and type field |
| security = ""; |
| error = true; |
| break; |
| } |
| |
| ByteBuffer buf = ByteBuffer.wrap(ie.data); |
| |
| if (buf.getInt(offset) != 0x01F25000) { |
| //look for HS2.0 and WPA IE |
| security = ""; |
| continue; |
| } |
| |
| security = "[WPA"; //prep the string for WPA |
| |
| //version |
| if ((total_len - offset) < 2) { |
| //not enough space for version field |
| security = ""; |
| error = true; |
| break; |
| } |
| int val = 0; |
| if (0x0100 != buf.getShort(offset)) { |
| //incorrect version |
| security = ""; |
| error = true; |
| break; |
| } |
| offset += 2; |
| |
| |
| //group cipher |
| if ((total_len - offset) < 4) { |
| security = ""; //parse error on group cipher suite |
| error = true; |
| break; |
| } |
| offset += 4; //skip the group cipher |
| |
| |
| //pairwise cipher |
| if ((total_len - offset) < 2) { |
| security = ""; //parse error no pairwise cipher |
| error = true; |
| break; |
| } |
| val = buf.getShort(offset); |
| if ((total_len - offset) < (2 + val * 4)) { |
| security = ""; //parse error no pairwise cipher |
| error = true; |
| break; |
| } |
| offset += 2 + val * 4; //skip the pairwise ciphers |
| |
| //AKM |
| if ((total_len - offset) < 2) { |
| security = ""; //parse error no AKM |
| error = true; |
| break; |
| } |
| val = buf.getShort(offset); |
| if ((total_len - offset) < (2 + val * 4)) { |
| security = ""; //parse error no pairwise cipher |
| error = true; |
| break; |
| } |
| offset += 2; |
| if (val == 0) { |
| security += "-EAP"; //default AKM |
| } |
| for (int i = 0; i < val; i++) { |
| int akm = buf.getInt(offset); |
| boolean found = false; |
| switch (akm) { |
| case 0x01f25000: |
| security += found ? "+" : "-" + "EAP"; |
| found = true; |
| break; |
| case 0x02f25000: |
| security += found ? "+" : "-" + "PSK"; //PSK as 802.11-2012 11.6.1.2 |
| found = true; |
| break; |
| |
| } |
| offset += 4; |
| } |
| //we parsed what we want at this point |
| security += "]"; |
| } |
| } |
| |
| if (rsne_found == false && wpa_found == false && privacy) { |
| //private Beacon without an RSNE or WPA IE, hence WEP0 |
| capabilities += "[WEP]"; |
| } |
| |
| if (error) |
| return null; |
| else |
| return capabilities; |
| } |
| |
| |
| } |