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