• 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 
17 package com.android.settings.wifi;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.net.wifi.WifiManager;
24 import android.nfc.FormatException;
25 import android.nfc.NdefMessage;
26 import android.nfc.NdefRecord;
27 import android.nfc.NfcAdapter;
28 import android.nfc.Tag;
29 import android.nfc.tech.Ndef;
30 import android.os.Bundle;
31 import android.os.PowerManager;
32 import android.text.Editable;
33 import android.text.InputType;
34 import android.text.TextWatcher;
35 import android.util.Log;
36 import android.view.View;
37 import android.view.inputmethod.InputMethodManager;
38 import android.widget.Button;
39 import android.widget.CheckBox;
40 import android.widget.CompoundButton;
41 import android.widget.ProgressBar;
42 import android.widget.TextView;
43 
44 import com.android.settings.R;
45 import com.android.settingslib.wifi.AccessPoint;
46 
47 import java.io.IOException;
48 
49 class WriteWifiConfigToNfcDialog extends AlertDialog
50         implements TextWatcher, View.OnClickListener, CompoundButton.OnCheckedChangeListener {
51 
52     private static final String NFC_TOKEN_MIME_TYPE = "application/vnd.wfa.wsc";
53 
54     private static final String TAG = WriteWifiConfigToNfcDialog.class.getName().toString();
55     private static final String PASSWORD_FORMAT = "102700%s%s";
56     private static final int HEX_RADIX = 16;
57     private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
58     private static final String SECURITY = "security";
59 
60     private final PowerManager.WakeLock mWakeLock;
61 
62     private View mView;
63     private Button mSubmitButton;
64     private Button mCancelButton;
65     private TextView mPasswordView;
66     private TextView mLabelView;
67     private CheckBox mPasswordCheckBox;
68     private ProgressBar mProgressBar;
69     private WifiManager mWifiManager;
70     private String mWpsNfcConfigurationToken;
71     private Context mContext;
72     private int mSecurity;
73 
WriteWifiConfigToNfcDialog(Context context, int security)74     WriteWifiConfigToNfcDialog(Context context, int security) {
75         super(context);
76 
77         mContext = context;
78         mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
79                 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
80         mSecurity = security;
81         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
82     }
83 
WriteWifiConfigToNfcDialog(Context context, Bundle savedState)84     WriteWifiConfigToNfcDialog(Context context, Bundle savedState) {
85         super(context);
86 
87         mContext = context;
88         mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
89                 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
90         mSecurity = savedState.getInt(SECURITY);
91         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
92     }
93 
94     @Override
onCreate(Bundle savedInstanceState)95     public void onCreate(Bundle savedInstanceState) {
96         mView = getLayoutInflater().inflate(R.layout.write_wifi_config_to_nfc, null);
97 
98         setView(mView);
99         setInverseBackgroundForced(true);
100         setTitle(R.string.setup_wifi_nfc_tag);
101         setCancelable(true);
102         setButton(DialogInterface.BUTTON_NEUTRAL,
103                 mContext.getResources().getString(R.string.write_tag), (OnClickListener) null);
104         setButton(DialogInterface.BUTTON_NEGATIVE,
105                 mContext.getResources().getString(com.android.internal.R.string.cancel),
106                 (OnClickListener) null);
107 
108         mPasswordView = mView.findViewById(R.id.password);
109         mLabelView = mView.findViewById(R.id.password_label);
110         mPasswordView.addTextChangedListener(this);
111         mPasswordCheckBox = mView.findViewById(R.id.show_password);
112         mPasswordCheckBox.setOnCheckedChangeListener(this);
113         mProgressBar = mView.findViewById(R.id.progress_bar);
114 
115         super.onCreate(savedInstanceState);
116 
117         mSubmitButton = getButton(DialogInterface.BUTTON_NEUTRAL);
118         mSubmitButton.setOnClickListener(this);
119         mSubmitButton.setEnabled(false);
120 
121         mCancelButton = getButton(DialogInterface.BUTTON_NEGATIVE);
122     }
123 
124     @Override
onClick(View v)125     public void onClick(View v) {
126         mWakeLock.acquire();
127 
128         String password = mPasswordView.getText().toString();
129         String wpsNfcConfigurationToken = mWifiManager.getCurrentNetworkWpsNfcConfigurationToken();
130         String passwordHex = byteArrayToHexString(password.getBytes());
131 
132         String passwordLength = password.length() >= HEX_RADIX
133                 ? Integer.toString(password.length(), HEX_RADIX)
134                 : "0" + Character.forDigit(password.length(), HEX_RADIX);
135 
136         passwordHex = String.format(PASSWORD_FORMAT, passwordLength, passwordHex).toLowerCase();
137 
138         if (wpsNfcConfigurationToken != null && wpsNfcConfigurationToken.contains(passwordHex)) {
139             mWpsNfcConfigurationToken = wpsNfcConfigurationToken;
140 
141             Activity activity = getOwnerActivity();
142             NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
143 
144             nfcAdapter.enableReaderMode(activity, new NfcAdapter.ReaderCallback() {
145                 @Override
146                 public void onTagDiscovered(Tag tag) {
147                     handleWriteNfcEvent(tag);
148                 }
149             }, NfcAdapter.FLAG_READER_NFC_A |
150                     NfcAdapter.FLAG_READER_NFC_B |
151                     NfcAdapter.FLAG_READER_NFC_BARCODE |
152                     NfcAdapter.FLAG_READER_NFC_F |
153                     NfcAdapter.FLAG_READER_NFC_V,
154                     null);
155 
156             mPasswordView.setVisibility(View.GONE);
157             mPasswordCheckBox.setVisibility(View.GONE);
158             mSubmitButton.setVisibility(View.GONE);
159             InputMethodManager imm = (InputMethodManager)
160                     getOwnerActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
161             imm.hideSoftInputFromWindow(mPasswordView.getWindowToken(), 0);
162 
163             mLabelView.setText(R.string.status_awaiting_tap);
164 
165             mView.findViewById(R.id.password_layout).setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
166             mProgressBar.setVisibility(View.VISIBLE);
167         } else {
168             mLabelView.setText(R.string.status_invalid_password);
169         }
170     }
171 
saveState(Bundle state)172     public void saveState(Bundle state) {
173         state.putInt(SECURITY, mSecurity);
174     }
175 
handleWriteNfcEvent(Tag tag)176     private void handleWriteNfcEvent(Tag tag) {
177         Ndef ndef = Ndef.get(tag);
178 
179         if (ndef != null) {
180             if (ndef.isWritable()) {
181                 NdefRecord record = NdefRecord.createMime(
182                         NFC_TOKEN_MIME_TYPE,
183                         hexStringToByteArray(mWpsNfcConfigurationToken));
184                 try {
185                     ndef.connect();
186                     ndef.writeNdefMessage(new NdefMessage(record));
187                     getOwnerActivity().runOnUiThread(new Runnable() {
188                         @Override
189                         public void run() {
190                             mProgressBar.setVisibility(View.GONE);
191                         }
192                     });
193                     setViewText(mLabelView, R.string.status_write_success);
194                     setViewText(mCancelButton, com.android.internal.R.string.done_label);
195                 } catch (IOException e) {
196                     setViewText(mLabelView, R.string.status_failed_to_write);
197                     Log.e(TAG, "Unable to write Wi-Fi config to NFC tag.", e);
198                     return;
199                 } catch (FormatException e) {
200                     setViewText(mLabelView, R.string.status_failed_to_write);
201                     Log.e(TAG, "Unable to write Wi-Fi config to NFC tag.", e);
202                     return;
203                 }
204             } else {
205                 setViewText(mLabelView, R.string.status_tag_not_writable);
206                 Log.e(TAG, "Tag is not writable");
207             }
208         } else {
209             setViewText(mLabelView, R.string.status_tag_not_writable);
210             Log.e(TAG, "Tag does not support NDEF");
211         }
212     }
213 
214     @Override
dismiss()215     public void dismiss() {
216         if (mWakeLock.isHeld()) {
217             mWakeLock.release();
218         }
219 
220         super.dismiss();
221     }
222 
223     @Override
onTextChanged(CharSequence s, int start, int before, int count)224     public void onTextChanged(CharSequence s, int start, int before, int count) {
225         enableSubmitIfAppropriate();
226     }
227 
enableSubmitIfAppropriate()228     private void enableSubmitIfAppropriate() {
229 
230         if (mPasswordView != null) {
231             if (mSecurity == AccessPoint.SECURITY_WEP) {
232                 mSubmitButton.setEnabled(mPasswordView.length() > 0);
233             } else if (mSecurity == AccessPoint.SECURITY_PSK) {
234                 mSubmitButton.setEnabled(mPasswordView.length() >= 8);
235             }
236         } else {
237             mSubmitButton.setEnabled(false);
238         }
239 
240     }
241 
setViewText(final TextView view, final int resid)242     private void setViewText(final TextView view, final int resid) {
243         getOwnerActivity().runOnUiThread(new Runnable() {
244             @Override
245             public void run() {
246                 view.setText(resid);
247             }
248         });
249     }
250 
251     @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)252     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
253         mPasswordView.setInputType(
254                 InputType.TYPE_CLASS_TEXT |
255                 (isChecked
256                         ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
257                         : InputType.TYPE_TEXT_VARIATION_PASSWORD));
258     }
259 
hexStringToByteArray(String s)260     private static byte[] hexStringToByteArray(String s) {
261         int len = s.length();
262         byte[] data = new byte[len / 2];
263 
264         for (int i = 0; i < len; i += 2) {
265             data[i / 2] = (byte) ((Character.digit(s.charAt(i), HEX_RADIX) << 4)
266                     + Character.digit(s.charAt(i + 1), HEX_RADIX));
267         }
268 
269         return data;
270     }
271 
byteArrayToHexString(byte[] bytes)272     private static String byteArrayToHexString(byte[] bytes) {
273         char[] hexChars = new char[bytes.length * 2];
274         for ( int j = 0; j < bytes.length; j++ ) {
275             int v = bytes[j] & 0xFF;
276             hexChars[j * 2] = hexArray[v >>> 4];
277             hexChars[j * 2 + 1] = hexArray[v & 0x0F];
278         }
279         return new String(hexChars);
280     }
281 
282     @Override
beforeTextChanged(CharSequence s, int start, int count, int after)283     public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
284 
285     @Override
afterTextChanged(Editable s)286     public void afterTextChanged(Editable s) {}
287 }
288