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.app.ActivityManager;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.net.wifi.WifiConfiguration;
     23 import android.nfc.NdefMessage;
     24 import android.nfc.NdefRecord;
     25 import android.nfc.tech.Ndef;
     26 import android.os.UserHandle;
     27 import android.os.UserManager;
     28 import android.util.Log;
     29 
     30 import java.nio.BufferUnderflowException;
     31 import java.nio.ByteBuffer;
     32 import java.util.Arrays;
     33 import java.util.BitSet;
     34 
     35 public final class NfcWifiProtectedSetup {
     36 
     37     public static final String NFC_TOKEN_MIME_TYPE = "application/vnd.wfa.wsc";
     38 
     39     public static final String EXTRA_WIFI_CONFIG = "com.android.nfc.WIFI_CONFIG_EXTRA";
     40 
     41     /*
     42      * ID into configuration record for SSID and Network Key in hex.
     43      * Obtained from WFA Wifi Simple Configuration Technical Specification v2.0.2.1.
     44      */
     45     private static final short CREDENTIAL_FIELD_ID = 0x100E;
     46     private static final short SSID_FIELD_ID = 0x1045;
     47     private static final short NETWORK_KEY_FIELD_ID = 0x1027;
     48     private static final short AUTH_TYPE_FIELD_ID = 0x1003;
     49 
     50     private static final short AUTH_TYPE_EXPECTED_SIZE = 2;
     51 
     52     private static final short AUTH_TYPE_OPEN = 0;
     53     private static final short AUTH_TYPE_WPA_PSK = 0x0002;
     54     private static final short AUTH_TYPE_WPA_EAP =  0x0008;
     55     private static final short AUTH_TYPE_WPA2_EAP = 0x0010;
     56     private static final short AUTH_TYPE_WPA2_PSK = 0x0020;
     57 
     58     private static final int MAX_NETWORK_KEY_SIZE_BYTES = 64;
     59 
     60     private NfcWifiProtectedSetup() {}
     61 
     62     public static boolean tryNfcWifiSetup(Ndef ndef, Context context) {
     63 
     64         if (ndef == null || context == null) {
     65             return false;
     66         }
     67 
     68         NdefMessage cachedNdefMessage = ndef.getCachedNdefMessage();
     69         if (cachedNdefMessage == null) {
     70             return false;
     71         }
     72 
     73         final WifiConfiguration wifiConfiguration;
     74         try {
     75             wifiConfiguration = parse(cachedNdefMessage);
     76         } catch (BufferUnderflowException e) {
     77             // malformed payload
     78             return false;
     79         }
     80 
     81         if (wifiConfiguration != null &&!UserManager.get(context).hasUserRestriction(
     82                 UserManager.DISALLOW_CONFIG_WIFI,
     83                 // hasUserRestriction does not support UserHandle.CURRENT.
     84                 UserHandle.of(ActivityManager.getCurrentUser()))) {
     85             Intent configureNetworkIntent = new Intent()
     86                     .putExtra(EXTRA_WIFI_CONFIG, wifiConfiguration)
     87                     .setClass(context, ConfirmConnectToWifiNetworkActivity.class)
     88                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
     89 
     90             context.startActivityAsUser(configureNetworkIntent, UserHandle.CURRENT);
     91             return true;
     92         }
     93 
     94         return false;
     95     }
     96 
     97     private static WifiConfiguration parse(NdefMessage message) {
     98         NdefRecord[] records = message.getRecords();
     99 
    100         for (NdefRecord record : records) {
    101             if (new String(record.getType()).equals(NFC_TOKEN_MIME_TYPE)) {
    102                 ByteBuffer payload = ByteBuffer.wrap(record.getPayload());
    103                 while (payload.hasRemaining()) {
    104                     short fieldId = payload.getShort();
    105                     int fieldSize = payload.getShort() & 0xFFFF;
    106                     if (fieldId == CREDENTIAL_FIELD_ID) {
    107                         return parseCredential(payload, fieldSize);
    108                     }
    109                     payload.position(payload.position() + fieldSize);
    110                 }
    111             }
    112         }
    113         return null;
    114     }
    115 
    116     private static WifiConfiguration parseCredential(ByteBuffer payload, int size) {
    117         int startPosition = payload.position();
    118         WifiConfiguration result = new WifiConfiguration();
    119         while (payload.position() < startPosition + size) {
    120             short fieldId = payload.getShort();
    121             int fieldSize = payload.getShort() & 0xFFFF;
    122 
    123             // sanity check
    124             if (payload.position() + fieldSize > startPosition + size) {
    125                 return null;
    126             }
    127 
    128             switch (fieldId) {
    129                 case SSID_FIELD_ID:
    130                     byte[] ssid = new byte[fieldSize];
    131                     payload.get(ssid);
    132                     result.SSID = "\"" + new String(ssid) + "\"";
    133                     break;
    134                 case NETWORK_KEY_FIELD_ID:
    135                     if (fieldSize > MAX_NETWORK_KEY_SIZE_BYTES) {
    136                         return null;
    137                     }
    138                     byte[] networkKey = new byte[fieldSize];
    139                     payload.get(networkKey);
    140                     result.preSharedKey = "\"" + new String(networkKey) + "\"";
    141                     break;
    142                 case AUTH_TYPE_FIELD_ID:
    143                     if (fieldSize != AUTH_TYPE_EXPECTED_SIZE) {
    144                         // corrupt data
    145                         return null;
    146                     }
    147 
    148                     short authType = payload.getShort();
    149                     populateAllowedKeyManagement(result.allowedKeyManagement, authType);
    150                     break;
    151                 default:
    152                     // unknown / unparsed tag
    153                     payload.position(payload.position() + fieldSize);
    154                     break;
    155             }
    156         }
    157 
    158         if (result.preSharedKey != null && result.SSID != null) {
    159             return result;
    160         }
    161 
    162         return null;
    163     }
    164 
    165     private static void populateAllowedKeyManagement(BitSet allowedKeyManagement, short authType) {
    166         if (authType == AUTH_TYPE_WPA_PSK || authType == AUTH_TYPE_WPA2_PSK) {
    167             allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
    168         } else if (authType == AUTH_TYPE_WPA_EAP || authType == AUTH_TYPE_WPA2_EAP) {
    169             allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
    170         } else if (authType == AUTH_TYPE_OPEN) {
    171             allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    172         }
    173     }
    174 }
    175