Write wifi config to NFC tag
Configured networks expose the option to write config to an NFC tag. This tag can be then tapped to another device to configure the same network. Implemented according to WiFi Alliance WPS spec. Change-Id: I33a1be1610aab71cf1ab864418d494027370ebca
This commit is contained in:
264
src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
Normal file
264
src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
Normal file
@@ -0,0 +1,264 @@
|
||||
package com.android.settings.wifi;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
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.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
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 final PowerManager.WakeLock mWakeLock;
|
||||
|
||||
private AccessPoint mAccessPoint;
|
||||
private View mView;
|
||||
private Button mSubmitButton;
|
||||
private Button mCancelButton;
|
||||
private Handler mOnTextChangedHandler;
|
||||
private TextView mPasswordView;
|
||||
private TextView mLabelView;
|
||||
private CheckBox mPasswordCheckBox;
|
||||
private ProgressBar mProgressBar;
|
||||
private WifiManager mWifiManager;
|
||||
private String mWpsNfcConfigurationToken;
|
||||
private Context mContext;
|
||||
|
||||
WriteWifiConfigToNfcDialog(Context context, AccessPoint accessPoint,
|
||||
WifiManager wifiManager) {
|
||||
super(context);
|
||||
this.mContext = context;
|
||||
this.mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
|
||||
this.mAccessPoint = accessPoint;
|
||||
this.mOnTextChangedHandler = new Handler();
|
||||
this.mWifiManager = wifiManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
mView = getLayoutInflater().inflate(R.layout.write_wifi_config_to_nfc, null);
|
||||
|
||||
setView(mView);
|
||||
setInverseBackgroundForced(true);
|
||||
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 = (TextView) mView.findViewById(R.id.password);
|
||||
mLabelView = (TextView) mView.findViewById(R.id.password_label);
|
||||
mPasswordView.addTextChangedListener(this);
|
||||
mPasswordCheckBox = (CheckBox) mView.findViewById(R.id.show_password);
|
||||
mPasswordCheckBox.setOnCheckedChangeListener(this);
|
||||
mProgressBar = (ProgressBar) 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.getWpsNfcConfigurationToken(mAccessPoint.networkId);
|
||||
String passwordHex = byteArrayToHexString(password.getBytes());
|
||||
|
||||
String passwordLength = password.length() >= 16
|
||||
? "" + Character.forDigit(password.length(), 16)
|
||||
: "0" + Character.forDigit(password.length(), 16);
|
||||
|
||||
passwordHex = String.format(PASSWORD_FORMAT, passwordLength, passwordHex).toUpperCase();
|
||||
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
|
||||
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 WiFi config to NFC tag.", e);
|
||||
return;
|
||||
} catch (FormatException e) {
|
||||
setViewText(mLabelView, R.string.status_failed_to_write);
|
||||
Log.e(TAG, "Unable to write WiFi 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) {
|
||||
mOnTextChangedHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
enableSubmitIfAppropriate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enableSubmitIfAppropriate() {
|
||||
|
||||
if (mPasswordView != null) {
|
||||
if (mAccessPoint.security == AccessPoint.SECURITY_WEP) {
|
||||
mSubmitButton.setEnabled(mPasswordView.length() > 0);
|
||||
} else if (mAccessPoint.security == 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), 16) << 4)
|
||||
+ Character.digit(s.charAt(i + 1), 16));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
|
||||
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) {}
|
||||
}
|
Reference in New Issue
Block a user