blob: 55ad18301ab7cfe909a68e16af113a4f2ebd5cb0 [file] [log] [blame]
/*
* 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) {}
}