Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd0e0d79 authored by Quang Luong's avatar Quang Luong Committed by Android (Google) Code Review
Browse files

Merge "Removed Write to NFC dialog"

parents e4f0c78b 83b81474
Loading
Loading
Loading
Loading
+0 −22
Original line number Diff line number Diff line
@@ -2023,8 +2023,6 @@
    <string name="wifi_menu_forget">Forget network</string>
    <!-- Menu option to modify a Wi-Fi network configuration -->
    <string name="wifi_menu_modify">Modify network</string>
    <!-- Menu option to write a Wi-Fi configuration token to an NFC tag [CHAR_LIMIT=30]-->
    <string name="wifi_menu_write_to_nfc">Write to NFC tag</string>
    <!-- Wi-Fi settings. text displayed when Wi-Fi is off and network list is empty [CHAR LIMIT=50]-->
    <string name="wifi_empty_list_wifi_off">To see available networks, turn Wi\u2011Fi on.</string>
    <!-- Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
@@ -2210,8 +2208,6 @@
    <!-- Substring of wifi status when Wi-Fi Protected Setup (WPS) is available and
         string is listed after a wifi_secured_* string-->
    <string name="wifi_wps_available_second_item">\u0020(WPS available)</string>
    <!-- Message in WriteWifiConfigToNfcDialog when prompted to enter network password [CHAR_LIMIT=40] -->
    <string name="wifi_wps_nfc_enter_password">Enter your network password</string>
    <!-- Message in dialog when the user tries to connect to a carrier network[CHAR_LIMIT=40] -->
    <string name="wifi_carrier_connect">Carrier Wi\u2011Fi network</string>
@@ -7283,24 +7279,6 @@
    <!-- Battery Saver: Search terms for sticky battery saver preference [CHAR_LIMIT=NONE] -->
    <string name="keywords_battery_saver_sticky">battery saver, sticky, persist, power saver, battery</string>
    <!-- NFC Wi-Fi pairing/setup strings-->
    <!-- Write NFC tag for Wi-Fi pairing/setup title [CHAR_LIMIT=30]-->
    <string name="setup_wifi_nfc_tag">Set up Wi-Fi NFC Tag</string>
    <!-- Text for button to confirm writing tag -->
    <string name="write_tag">Write</string>
    <!-- Text to inform the user to tap a tag to complete the setup process -->
    <string name="status_awaiting_tap">Tap a tag to write...</string>
    <!-- Text to inform the user that the network key entered was incorrect -->
    <string name="status_invalid_password">Invalid password, try again.</string>
    <!-- Text displayed when tag successfully writen -->
    <string name="status_write_success">Success!</string>
    <!-- Text displayed in error cases (failure to write to tag) -->
    <string name="status_failed_to_write">Unable to write data to NFC tag. If the problem persists, try a different tag</string>
    <!-- Text displayed when tag is not writable -->
    <string name="status_tag_not_writable">NFC tag is not writable. Please use a different tag.</string>
    <!-- Option title for the default sound, context based on screen -->
    <string name="default_sound">Default sound</string>
+0 −41
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@ import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -100,10 +99,8 @@ public class WifiSettings extends RestrictedSettingsFragment
    private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
    private static final int MENU_ID_FORGET = Menu.FIRST + 7;
    private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
    private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9;

    public static final int WIFI_DIALOG_ID = 1;
    private static final int WRITE_NFC_DIALOG_ID = 6;

    @VisibleForTesting
    static final int ADD_NETWORK_REQUEST = 2;
@@ -111,7 +108,6 @@ public class WifiSettings extends RestrictedSettingsFragment
    // Instance state keys
    private static final String SAVE_DIALOG_MODE = "dialog_mode";
    private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
    private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state";

    private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
    private static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point";
@@ -152,7 +148,6 @@ public class WifiSettings extends RestrictedSettingsFragment
    private AccessPoint mSelectedAccessPoint;

    private WifiDialog mDialog;
    private WriteWifiConfigToNfcDialog mWifiToNfcDialog;

    private View mProgressHeader;

@@ -171,7 +166,6 @@ public class WifiSettings extends RestrictedSettingsFragment
    private int mDialogMode;
    private AccessPoint mDlgAccessPoint;
    private Bundle mAccessPointSavedState;
    private Bundle mWifiNfcDialogSavedState;

    @VisibleForTesting
    WifiTracker mWifiTracker;
@@ -298,11 +292,6 @@ public class WifiSettings extends RestrictedSettingsFragment
                mAccessPointSavedState =
                        savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
            }

            if (savedInstanceState.containsKey(SAVED_WIFI_NFC_DIALOG_STATE)) {
                mWifiNfcDialogSavedState =
                        savedInstanceState.getBundle(SAVED_WIFI_NFC_DIALOG_STATE);
            }
        }

        // if we're supposed to enable/disable the Next button based on our current connection
@@ -447,12 +436,6 @@ public class WifiSettings extends RestrictedSettingsFragment
                outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
            }
        }

        if (mWifiToNfcDialog != null) {
            Bundle savedState = new Bundle();
            mWifiToNfcDialog.saveState(savedState);
            outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState);
        }
    }

    @Override
@@ -481,13 +464,6 @@ public class WifiSettings extends RestrictedSettingsFragment
            }
            if (mSelectedAccessPoint.isSaved()) {
                menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
                NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
                if (nfcAdapter != null && nfcAdapter.isEnabled() &&
                        (!(mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) ||
                                (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_OWE))) {
                    // Only allow writing of NFC tags for password-protected networks.
                    menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc);
                }
            }
        }
    }
@@ -520,10 +496,6 @@ public class WifiSettings extends RestrictedSettingsFragment
                showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY);
                return true;
            }
            case MENU_ID_WRITE_NFC:
                showDialog(WRITE_NFC_DIALOG_ID);
                return true;

        }
        return super.onContextItemSelected(item);
    }
@@ -612,16 +584,6 @@ public class WifiSettings extends RestrictedSettingsFragment
                        .createModal(getActivity(), this, mDlgAccessPoint, mDialogMode);
                mSelectedAccessPoint = mDlgAccessPoint;
                return mDialog;
            case WRITE_NFC_DIALOG_ID:
                if (mSelectedAccessPoint != null) {
                    mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
                            getActivity(),
                            mSelectedAccessPoint.getSecurity());
                } else if (mWifiNfcDialogSavedState != null) {
                    mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(getActivity(),
                            mWifiNfcDialogSavedState);
                }
                return mWifiToNfcDialog;
        }
        return super.onCreateDialog(dialogId);
    }
@@ -636,7 +598,6 @@ public class WifiSettings extends RestrictedSettingsFragment
    public void onDismiss(DialogInterface dialog) {
        // We don't keep any dialog object when dialog was dismissed.
        mDialog = null;
        mWifiToNfcDialog = null;
    }

    @Override
@@ -644,8 +605,6 @@ public class WifiSettings extends RestrictedSettingsFragment
        switch (dialogId) {
            case WIFI_DIALOG_ID:
                return SettingsEnums.DIALOG_WIFI_AP_EDIT;
            case WRITE_NFC_DIALOG_ID:
                return SettingsEnums.DIALOG_WIFI_WRITE_NFC;
            default:
                return 0;
        }
+0 −287
Original line number Diff line number Diff line
/*
 * 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 androidx.appcompat.app.AlertDialog;

import com.android.settings.R;
import com.android.settingslib.wifi.AccessPoint;

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 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) {}
}
+0 −90
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.when;

import android.app.Activity;
import android.net.wifi.WifiManager;

import com.android.settings.testutils.shadow.ShadowNfcAdapter;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;

@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowNfcAdapter.class)
public class WriteWifiConfigToNfcDialogTest {

    @Mock
    private WifiManager mWifiManager;

    private WriteWifiConfigToNfcDialog mWriteWifiConfigToNfcDialog;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        final Activity activity = Robolectric.setupActivity(Activity.class);
        mWriteWifiConfigToNfcDialog = new WriteWifiConfigToNfcDialog(activity, 0 /* security */);
        ReflectionHelpers.setField(mWriteWifiConfigToNfcDialog, "mWifiManager", mWifiManager);
        mWriteWifiConfigToNfcDialog.setOwnerActivity(activity);
        mWriteWifiConfigToNfcDialog.onCreate(null /* savedInstanceState */);
    }

    @After
    public void tearDown() {
        ShadowNfcAdapter.reset();
    }

    @Test
    public void testOnClick_nfcConfigurationTokenDoesNotContainPasswordHex() {
        when(mWifiManager.getCurrentNetworkWpsNfcConfigurationToken()).thenReturn("blah");

        mWriteWifiConfigToNfcDialog.onClick(null);

        assertThat(ShadowNfcAdapter.isReaderModeEnabled()).isFalse();
    }

    @Test
    public void testOnClick_nfcConfigurationTokenIsNull() {
        when(mWifiManager.getCurrentNetworkWpsNfcConfigurationToken()).thenReturn(null);

        mWriteWifiConfigToNfcDialog.onClick(null);

        assertThat(ShadowNfcAdapter.isReaderModeEnabled()).isFalse();
    }

    @Test
    public void testOnClick_nfcConfigurationTokenContainsPasswordHex() {
        // This is the corresponding passwordHex for an empty string password.
        when(mWifiManager.getCurrentNetworkWpsNfcConfigurationToken()).thenReturn("10270000");

        mWriteWifiConfigToNfcDialog.onClick(null);

        assertThat(ShadowNfcAdapter.isReaderModeEnabled()).isTrue();
    }
}