| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.settings.wifi; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.net.wifi.WifiManager; |
| import android.nfc.FormatException; |
| import android.nfc.NdefMessage; |
| import android.nfc.NdefRecord; |
| import android.nfc.NfcAdapter; |
| import android.nfc.Tag; |
| import android.nfc.tech.Ndef; |
| import android.os.Bundle; |
| import android.os.PowerManager; |
| import android.text.Editable; |
| import android.text.InputType; |
| import android.text.TextWatcher; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.inputmethod.InputMethodManager; |
| import android.widget.Button; |
| import android.widget.CheckBox; |
| import android.widget.CompoundButton; |
| import android.widget.ProgressBar; |
| import android.widget.TextView; |
| |
| import com.android.settings.R; |
| import com.android.settingslib.wifi.AccessPoint; |
| |
| import java.io.IOException; |
| |
| import androidx.appcompat.app.AlertDialog; |
| |
| class WriteWifiConfigToNfcDialog extends AlertDialog |
| implements TextWatcher, View.OnClickListener, CompoundButton.OnCheckedChangeListener { |
| |
| private static final String NFC_TOKEN_MIME_TYPE = "application/vnd.wfa.wsc"; |
| |
| private static final String TAG = WriteWifiConfigToNfcDialog.class.getName().toString(); |
| private static final String PASSWORD_FORMAT = "102700%s%s"; |
| private static final int HEX_RADIX = 16; |
| private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); |
| private static final String SECURITY = "security"; |
| |
| private final PowerManager.WakeLock mWakeLock; |
| |
| private View mView; |
| private Button mSubmitButton; |
| private Button mCancelButton; |
| private TextView mPasswordView; |
| private TextView mLabelView; |
| private CheckBox mPasswordCheckBox; |
| private ProgressBar mProgressBar; |
| private WifiManager mWifiManager; |
| private String mWpsNfcConfigurationToken; |
| private Context mContext; |
| private int mSecurity; |
| |
| WriteWifiConfigToNfcDialog(Context context, int security) { |
| super(context); |
| |
| mContext = context; |
| mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)) |
| .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock"); |
| mSecurity = security; |
| mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); |
| } |
| |
| WriteWifiConfigToNfcDialog(Context context, Bundle savedState) { |
| super(context); |
| |
| mContext = context; |
| mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)) |
| .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock"); |
| mSecurity = savedState.getInt(SECURITY); |
| mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| mView = getLayoutInflater().inflate(R.layout.write_wifi_config_to_nfc, null); |
| |
| setView(mView); |
| setTitle(R.string.setup_wifi_nfc_tag); |
| setCancelable(true); |
| setButton(DialogInterface.BUTTON_NEUTRAL, |
| mContext.getResources().getString(R.string.write_tag), (OnClickListener) null); |
| setButton(DialogInterface.BUTTON_NEGATIVE, |
| mContext.getResources().getString(com.android.internal.R.string.cancel), |
| (OnClickListener) null); |
| |
| mPasswordView = mView.findViewById(R.id.password); |
| mLabelView = mView.findViewById(R.id.password_label); |
| mPasswordView.addTextChangedListener(this); |
| mPasswordCheckBox = mView.findViewById(R.id.show_password); |
| mPasswordCheckBox.setOnCheckedChangeListener(this); |
| mProgressBar = mView.findViewById(R.id.progress_bar); |
| |
| super.onCreate(savedInstanceState); |
| |
| mSubmitButton = getButton(DialogInterface.BUTTON_NEUTRAL); |
| mSubmitButton.setOnClickListener(this); |
| mSubmitButton.setEnabled(false); |
| |
| mCancelButton = getButton(DialogInterface.BUTTON_NEGATIVE); |
| } |
| |
| @Override |
| public void onClick(View v) { |
| mWakeLock.acquire(); |
| |
| String password = mPasswordView.getText().toString(); |
| String wpsNfcConfigurationToken = mWifiManager.getCurrentNetworkWpsNfcConfigurationToken(); |
| String passwordHex = byteArrayToHexString(password.getBytes()); |
| |
| String passwordLength = password.length() >= HEX_RADIX |
| ? Integer.toString(password.length(), HEX_RADIX) |
| : "0" + Character.forDigit(password.length(), HEX_RADIX); |
| |
| passwordHex = String.format(PASSWORD_FORMAT, passwordLength, passwordHex).toLowerCase(); |
| |
| if (wpsNfcConfigurationToken != null && wpsNfcConfigurationToken.contains(passwordHex)) { |
| mWpsNfcConfigurationToken = wpsNfcConfigurationToken; |
| |
| Activity activity = getOwnerActivity(); |
| NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity); |
| |
| nfcAdapter.enableReaderMode(activity, new NfcAdapter.ReaderCallback() { |
| @Override |
| public void onTagDiscovered(Tag tag) { |
| handleWriteNfcEvent(tag); |
| } |
| }, NfcAdapter.FLAG_READER_NFC_A | |
| NfcAdapter.FLAG_READER_NFC_B | |
| NfcAdapter.FLAG_READER_NFC_BARCODE | |
| NfcAdapter.FLAG_READER_NFC_F | |
| NfcAdapter.FLAG_READER_NFC_V, |
| null); |
| |
| mPasswordView.setVisibility(View.GONE); |
| mPasswordCheckBox.setVisibility(View.GONE); |
| mSubmitButton.setVisibility(View.GONE); |
| InputMethodManager imm = (InputMethodManager) |
| getOwnerActivity().getSystemService(Context.INPUT_METHOD_SERVICE); |
| imm.hideSoftInputFromWindow(mPasswordView.getWindowToken(), 0); |
| |
| mLabelView.setText(R.string.status_awaiting_tap); |
| |
| mView.findViewById(R.id.password_layout).setTextAlignment(View.TEXT_ALIGNMENT_CENTER); |
| mProgressBar.setVisibility(View.VISIBLE); |
| } else { |
| mLabelView.setText(R.string.status_invalid_password); |
| } |
| } |
| |
| public void saveState(Bundle state) { |
| state.putInt(SECURITY, mSecurity); |
| } |
| |
| private void handleWriteNfcEvent(Tag tag) { |
| Ndef ndef = Ndef.get(tag); |
| |
| if (ndef != null) { |
| if (ndef.isWritable()) { |
| NdefRecord record = NdefRecord.createMime( |
| NFC_TOKEN_MIME_TYPE, |
| hexStringToByteArray(mWpsNfcConfigurationToken)); |
| try { |
| ndef.connect(); |
| ndef.writeNdefMessage(new NdefMessage(record)); |
| getOwnerActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mProgressBar.setVisibility(View.GONE); |
| } |
| }); |
| setViewText(mLabelView, R.string.status_write_success); |
| setViewText(mCancelButton, com.android.internal.R.string.done_label); |
| } catch (IOException e) { |
| setViewText(mLabelView, R.string.status_failed_to_write); |
| Log.e(TAG, "Unable to write Wi-Fi config to NFC tag.", e); |
| return; |
| } catch (FormatException e) { |
| setViewText(mLabelView, R.string.status_failed_to_write); |
| Log.e(TAG, "Unable to write Wi-Fi config to NFC tag.", e); |
| return; |
| } |
| } else { |
| setViewText(mLabelView, R.string.status_tag_not_writable); |
| Log.e(TAG, "Tag is not writable"); |
| } |
| } else { |
| setViewText(mLabelView, R.string.status_tag_not_writable); |
| Log.e(TAG, "Tag does not support NDEF"); |
| } |
| } |
| |
| @Override |
| public void dismiss() { |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| |
| super.dismiss(); |
| } |
| |
| @Override |
| public void onTextChanged(CharSequence s, int start, int before, int count) { |
| enableSubmitIfAppropriate(); |
| } |
| |
| private void enableSubmitIfAppropriate() { |
| |
| if (mPasswordView != null) { |
| if (mSecurity == AccessPoint.SECURITY_WEP) { |
| mSubmitButton.setEnabled(mPasswordView.length() > 0); |
| } else if (mSecurity == AccessPoint.SECURITY_PSK) { |
| mSubmitButton.setEnabled(mPasswordView.length() >= 8); |
| } |
| } else { |
| mSubmitButton.setEnabled(false); |
| } |
| |
| } |
| |
| private void setViewText(final TextView view, final int resid) { |
| getOwnerActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| view.setText(resid); |
| } |
| }); |
| } |
| |
| @Override |
| public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
| mPasswordView.setInputType( |
| InputType.TYPE_CLASS_TEXT | |
| (isChecked |
| ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD |
| : InputType.TYPE_TEXT_VARIATION_PASSWORD)); |
| } |
| |
| private static byte[] hexStringToByteArray(String s) { |
| int len = s.length(); |
| byte[] data = new byte[len / 2]; |
| |
| for (int i = 0; i < len; i += 2) { |
| data[i / 2] = (byte) ((Character.digit(s.charAt(i), HEX_RADIX) << 4) |
| + Character.digit(s.charAt(i + 1), HEX_RADIX)); |
| } |
| |
| return data; |
| } |
| |
| private static String byteArrayToHexString(byte[] bytes) { |
| char[] hexChars = new char[bytes.length * 2]; |
| for ( int j = 0; j < bytes.length; j++ ) { |
| int v = bytes[j] & 0xFF; |
| hexChars[j * 2] = hexArray[v >>> 4]; |
| hexChars[j * 2 + 1] = hexArray[v & 0x0F]; |
| } |
| return new String(hexChars); |
| } |
| |
| @Override |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) {} |
| |
| @Override |
| public void afterTextChanged(Editable s) {} |
| } |