• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
NfcWifiProtectedSetup()48     private NfcWifiProtectedSetup() {}
49 
tryNfcWifiSetup(Ndef ndef, Context context)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 
parse(NdefMessage message)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 
hexStringToInt(String bigEndianHexString)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 
bytesToHex(byte[] bytes)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