Home | History | Annotate | Download | only in nfc
      1 /*
      2 * Copyright (C) 2014 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 package com.android.nfc;
     17 
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.net.wifi.WifiConfiguration;
     22 import android.nfc.NdefMessage;
     23 import android.nfc.NdefRecord;
     24 import android.nfc.tech.Ndef;
     25 import android.os.UserHandle;
     26 import android.os.UserManager;
     27 
     28 import java.util.Arrays;
     29 
     30 public final class NfcWifiProtectedSetup {
     31 
     32     public static final String NFC_TOKEN_MIME_TYPE = "application/vnd.wfa.wsc";
     33 
     34     public static final String EXTRA_WIFI_CONFIG = "com.android.nfc.WIFI_CONFIG_EXTRA";
     35 
     36     /*
     37      * ID into configuration record for SSID and Network Key in hex.
     38      * Obtained from WFA Wifi Simple Configuration Technical Specification v2.0.2.1.
     39      */
     40     private static final String SSID_ID = "1045";
     41     private static final String NETWORK_KEY_ID = "1027";
     42 
     43     private static final int SIZE_FIELD_WIDTH = 4;
     44     private static final int MAX_SSID_SIZE_BYTES = 32;
     45     private static final int MAX_NETWORK_KEY_SIZE_BYTES = 64;
     46     private static final int HEX_CHARS_PER_BYTE = 2;
     47 
     48     private NfcWifiProtectedSetup() {}
     49 
     50     public static boolean tryNfcWifiSetup(Ndef ndef, Context context) {
     51 
     52         if (ndef == null || context == null) {
     53             return false;
     54         }
     55 
     56         NdefMessage cachedNdefMessage = ndef.getCachedNdefMessage();
     57         if (cachedNdefMessage == null) {
     58             return false;
     59         }
     60 
     61         final WifiConfiguration wifiConfiguration = parse(cachedNdefMessage);
     62 
     63         if (wifiConfiguration != null &&!UserManager.get(context).hasUserRestriction(
     64                 UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {
     65             Intent configureNetworkIntent = new Intent()
     66                     .putExtra(EXTRA_WIFI_CONFIG, wifiConfiguration)
     67                     .setClass(context, ConfirmConnectToWifiNetworkActivity.class)
     68                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     69 
     70             context.startActivityAsUser(configureNetworkIntent, UserHandle.CURRENT);
     71             return true;
     72         }
     73 
     74         return false;
     75     }
     76 
     77     private static WifiConfiguration parse(NdefMessage message) {
     78         NdefRecord[] records = message.getRecords();
     79 
     80         for (int i = 0; i < records.length; ++i) {
     81             NdefRecord record = records[i];
     82             if (new String(record.getType()).equals(NFC_TOKEN_MIME_TYPE)) {
     83                 String hexStringPayload = bytesToHex(record.getPayload());
     84 
     85                 int ssidStringIndex = hexStringPayload.indexOf(SSID_ID);
     86 
     87                 if (ssidStringIndex > 0) {
     88                     int networkKeyStringIndex = hexStringPayload.indexOf(NETWORK_KEY_ID);
     89                     if (networkKeyStringIndex > 0) {
     90 
     91                         ssidStringIndex += SSID_ID.length();
     92                         networkKeyStringIndex += NETWORK_KEY_ID.length();
     93 
     94                         String ssidSize;
     95                         try {
     96                             ssidSize = hexStringPayload.substring(ssidStringIndex,
     97                                     ssidStringIndex + SIZE_FIELD_WIDTH);
     98                         } catch(IndexOutOfBoundsException ex) {
     99                             return null;
    100                         }
    101 
    102                         int ssidSizeBytes = hexStringToInt(ssidSize);
    103                         if (ssidSizeBytes > MAX_SSID_SIZE_BYTES) {
    104                             return null;
    105                         }
    106 
    107                         String networkKeySize;
    108                         try {
    109                             networkKeySize = hexStringPayload.substring(networkKeyStringIndex,
    110                                     networkKeyStringIndex + SIZE_FIELD_WIDTH);
    111                         } catch (IndexOutOfBoundsException ex) {
    112                             return null;
    113                         }
    114 
    115                         int networkKeySizeBytes = hexStringToInt(networkKeySize);
    116                         if (networkKeySizeBytes > MAX_NETWORK_KEY_SIZE_BYTES) {
    117                             return null;
    118                         }
    119 
    120                         ssidStringIndex += SIZE_FIELD_WIDTH;
    121                         networkKeyStringIndex += SIZE_FIELD_WIDTH;
    122 
    123                         String ssid;
    124                         String networkKey;
    125                         try {
    126                             int ssidByteIndex = ssidStringIndex / HEX_CHARS_PER_BYTE;
    127                             ssid = new String(Arrays.copyOfRange(record.getPayload(), ssidByteIndex,
    128                                     ssidByteIndex + ssidSizeBytes));
    129 
    130                             int networkKeyByteIndex = networkKeyStringIndex / HEX_CHARS_PER_BYTE;
    131                             networkKey = new String(Arrays.copyOfRange(record.getPayload(),
    132                                             networkKeyByteIndex,
    133                                             networkKeyByteIndex + networkKeySizeBytes));
    134                         } catch (ArrayIndexOutOfBoundsException ex) {
    135                             return null;
    136                         }
    137 
    138                         WifiConfiguration configuration = new WifiConfiguration();
    139                         configuration.preSharedKey = '"' + networkKey + '"';
    140                         configuration.SSID = '"' + ssid + '"';
    141 
    142                         return configuration;
    143                     }
    144                 }
    145             }
    146         }
    147 
    148         return null;
    149     }
    150 
    151     private static int hexStringToInt(String bigEndianHexString) {
    152         int val = 0;
    153 
    154         for (int i = 0; i < bigEndianHexString.length(); ++i) {
    155             val = (val | Character.digit(bigEndianHexString.charAt(i), 16));
    156 
    157             if (i < bigEndianHexString.length() - 1) {
    158                 val <<= 4;
    159             }
    160         }
    161 
    162         return val;
    163     }
    164 
    165     private static String bytesToHex(byte[] bytes) {
    166         char[] hexChars = new char[bytes.length * 2];
    167         for ( int j = 0; j < bytes.length; j++) {
    168             int value = bytes[j] & 0xFF;
    169             hexChars[j * 2] = Character.forDigit(value >>> 4, 16);
    170             hexChars[j * 2 + 1] = Character.forDigit(value & 0x0F, 16);
    171         }
    172         return new String(hexChars);
    173     }
    174 
    175 }
    176