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