diff --git a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml index 4eed0f64eba..0069e933852 100644 --- a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml +++ b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml @@ -46,9 +46,14 @@ android:layout_height="match_parent"/> - + android:layout_height="wrap_content" + android:textAlignment="center" + android:layout_marginTop="8dp" + android:visibility="invisible" + android:textColor="?android:attr/colorError"/> diff --git a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml index c5e416b258e..9bd066b8d17 100644 --- a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml +++ b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml @@ -50,8 +50,8 @@ android:id="@+id/error_message" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:textAlignment="center" android:layout_marginTop="8dp" - android:text="@string/wifi_dpp_could_not_detect_valid_qr_code" android:visibility="invisible" android:textColor="?android:attr/colorError"/> diff --git a/res/values/strings.xml b/res/values/strings.xml index 266836d60cd..6fadca55c33 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2124,7 +2124,19 @@ Scan this QR code with another device to join \u201c%1$s\u201d - Couldn\u2019t read QR code + Couldn\u2019t read QR code. Re-center code and try again + + Try again. If the issue continues, contact the device manufacturer + + Something went wrong + + Make sure the device has been plugged in, charged, and turned on + + Make sure the device has been plugged in, charged, and turned on. If the issue continues, contact the device manufacturer + + Adding \u201c%1$s\u201d isn\u2019t supported by this device + + Check connection and try again Choose network diff --git a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java index 4523defe023..536924d3413 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java +++ b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java @@ -20,6 +20,8 @@ import android.app.ActionBar; import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.net.wifi.EasyConnectStatusCallback; import android.net.wifi.WifiManager; import android.os.Bundle; import android.text.TextUtils; @@ -30,6 +32,8 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; +import androidx.lifecycle.ViewModelProviders; + import com.android.settings.R; import java.util.concurrent.Executor; @@ -46,7 +50,12 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment { private Button mButtonLeft; private Button mButtonRight; - private class EasyConnectStatusCallback extends android.net.wifi.EasyConnectStatusCallback { + private int mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_FAILURE_NONE; + + // Key for Bundle usage + private static final String KEY_LATEST_ERROR_CODE = "key_latest_error_code"; + + private class EasyConnectConfiguratorStatusCallback extends EasyConnectStatusCallback { @Override public void onEnrolleeSuccess(int newNetworkId) { // Do nothing @@ -54,32 +63,14 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment { @Override public void onConfiguratorSuccess(int code) { - // Update success UI. - setHeaderIconImageResource(R.drawable.ic_devices_check_circle_green); - mTitle.setText(R.string.wifi_dpp_wifi_shared_with_device); - mSummary.setVisibility(View.INVISIBLE); - mWifiApPictureView.setImageResource(R.drawable.wifi_dpp_success); - mChooseDifferentNetwork.setVisibility(View.INVISIBLE); - mButtonLeft.setText(R.string.wifi_dpp_add_another_device); - mButtonLeft.setOnClickListener(v -> getFragmentManager().popBackStack()); - mButtonRight.setText(R.string.done); - mButtonRight.setOnClickListener(v -> { - final Activity activity = getActivity(); - activity.setResult(Activity.RESULT_OK); - activity.finish(); - }); + showSuccessUi(/* isConfigurationChange */ false); } @Override public void onFailure(int code) { - Log.d(TAG, "EasyConnectStatusCallback.onFailure " + code); + Log.d(TAG, "EasyConnectConfiguratorStatusCallback.onFailure " + code); - // Update fail UI. - mTitle.setText(R.string.wifi_dpp_could_not_add_device); - mSummary.setVisibility(View.INVISIBLE); - mWifiApPictureView.setImageResource(R.drawable.wifi_dpp_error); - mChooseDifferentNetwork.setVisibility(View.INVISIBLE); - mButtonRight.setText(R.string.retry); + showErrorUi(code, /* isConfigurationChange */ false); } @Override @@ -88,11 +79,144 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment { } } + private void showSuccessUi(boolean isConfigurationChange) { + setHeaderIconImageResource(R.drawable.ic_devices_check_circle_green); + mTitle.setText(R.string.wifi_dpp_wifi_shared_with_device); + mSummary.setVisibility(View.INVISIBLE); + mWifiApPictureView.setImageResource(R.drawable.wifi_dpp_success); + mChooseDifferentNetwork.setVisibility(View.INVISIBLE); + mButtonLeft.setText(R.string.wifi_dpp_add_another_device); + mButtonLeft.setOnClickListener(v -> getFragmentManager().popBackStack()); + mButtonRight.setText(R.string.done); + mButtonRight.setOnClickListener(v -> { + final Activity activity = getActivity(); + activity.setResult(Activity.RESULT_OK); + activity.finish(); + }); + + if (!isConfigurationChange) { + mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS; + } + } + + private void showErrorUi(int code, boolean isConfigurationChange) { + switch (code) { + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI: + mSummary.setText(R.string.wifi_dpp_could_not_detect_valid_qr_code); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION: + mSummary.setText(R.string.wifi_dpp_failure_authentication_or_configuration); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE: + mSummary.setText(R.string.wifi_dpp_failure_not_compatible); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION: + mSummary.setText(R.string.wifi_dpp_failure_authentication_or_configuration); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY: + if (isConfigurationChange) { + return; + } + + if (code == mLatestStatusCode) { + throw(new IllegalStateException("Tried restarting EasyConnectSession but still" + + "receiving EASY_CONNECT_EVENT_FAILURE_BUSY")); + } + + mLatestStatusCode = code; + final WifiManager wifiManager = + getContext().getSystemService(WifiManager.class); + wifiManager.stopEasyConnectSession(); + startWifiDppConfiguratorInitiator(); + return; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT: + mSummary.setText(R.string.wifi_dpp_failure_timeout); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC: + mSummary.setText(R.string.wifi_dpp_failure_generic); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED: + mSummary.setText(getString(R.string.wifi_dpp_failure_not_supported, getSsid())); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK: + throw(new IllegalStateException("Wi-Fi DPP configurator used a non-PSK/non-SAE" + + "network to handshake")); + + default: + throw(new IllegalStateException("Unexpected Wi-Fi DPP error")); + } + + mTitle.setText(R.string.wifi_dpp_could_not_add_device); + mWifiApPictureView.setImageResource(R.drawable.wifi_dpp_error); + mChooseDifferentNetwork.setVisibility(View.INVISIBLE); + if (hasRetryButton(code)) { + mButtonRight.setText(R.string.retry); + } else { + mButtonRight.setText(R.string.done); + mButtonRight.setOnClickListener(v -> getActivity().finish()); + mButtonLeft.setVisibility(View.INVISIBLE); + } + + if (!isConfigurationChange) { + mLatestStatusCode = code; + } + + mButtonRight.setVisibility(isGoingInitiator() ? View.INVISIBLE : View.VISIBLE); + } + + private boolean hasRetryButton(int code) { + switch (code) { + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI: + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE: + return false; + + default: + break; + } + + return true; + } + @Override public int getMetricsCategory() { return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR; } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState != null) { + mLatestStatusCode = savedInstanceState.getInt(KEY_LATEST_ERROR_CODE); + } + + final WifiDppInitiatorViewModel model = + ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class); + + model.getStatusCode().observe(this, statusCode -> { + // After configuration change, observe callback will be triggered, + // do nothing for this case if a handshake does not end + if (model.isGoingInitiator()) { + return; + } + + int code = statusCode.intValue(); + if (code == WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS) { + new EasyConnectConfiguratorStatusCallback().onConfiguratorSuccess(code); + } else { + new EasyConnectConfiguratorStatusCallback().onFailure(code); + } + }); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -125,14 +249,7 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment { mTitle.setText(information); } - final WifiNetworkConfig wifiNetworkConfig = ((WifiDppConfiguratorActivity) getActivity()) - .getWifiNetworkConfig(); - if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) { - throw new IllegalStateException("Invalid Wi-Fi network for configuring"); - } - mSummary.setText(getString(R.string.wifi_dpp_add_device_to_wifi, - wifiNetworkConfig.getSsid())); - + mSummary.setText(getString(R.string.wifi_dpp_add_device_to_wifi, getSsid())); mWifiApPictureView = view.findViewById(R.id.wifi_ap_picture_view); mChooseDifferentNetwork = view.findViewById(R.id.choose_different_network); @@ -146,19 +263,48 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment { mButtonRight = view.findViewById(R.id.button_right); mButtonRight.setText(R.string.wifi_dpp_share_wifi); - mButtonRight.setOnClickListener(v -> startWifiDppInitiator()); + mButtonRight.setOnClickListener(v -> { + mButtonRight.setVisibility(View.INVISIBLE); + startWifiDppConfiguratorInitiator(); + }); + + if (savedInstanceState != null) { + if (mLatestStatusCode == WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS) { + showSuccessUi(/* isConfigurationChange */ true); + } else if (mLatestStatusCode == WifiDppUtils.EASY_CONNECT_EVENT_FAILURE_NONE) { + mButtonRight.setVisibility(isGoingInitiator() ? View.INVISIBLE : View.VISIBLE); + } else { + showErrorUi(mLatestStatusCode, /* isConfigurationChange */ true); + } + } } - private void startWifiDppInitiator() { + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putInt(KEY_LATEST_ERROR_CODE, mLatestStatusCode); + + super.onSaveInstanceState(outState); + } + + private String getSsid() { + final WifiNetworkConfig wifiNetworkConfig = ((WifiDppConfiguratorActivity) getActivity()) + .getWifiNetworkConfig(); + if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) { + throw new IllegalStateException("Invalid Wi-Fi network for configuring"); + } + return wifiNetworkConfig.getSsid(); + } + + private void startWifiDppConfiguratorInitiator() { final WifiQrCode wifiQrCode = ((WifiDppConfiguratorActivity) getActivity()) .getWifiDppQrCode(); final String qrCode = wifiQrCode.getQrCode(); final int networkId = ((WifiDppConfiguratorActivity) getActivity()).getWifiNetworkConfig().getNetworkId(); - final WifiManager wifiManager = getContext().getSystemService(WifiManager.class); - wifiManager.startEasyConnectAsConfiguratorInitiator(qrCode, networkId, - WifiManager.EASY_CONNECT_NETWORK_ROLE_STA, getContext().getMainExecutor(), - new EasyConnectStatusCallback()); + final WifiDppInitiatorViewModel model = + ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class); + + model.startEasyConnectAsConfiguratorInitiator(qrCode, networkId); } // Container Activity must implement this interface @@ -180,4 +326,12 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment { super.onDetach(); } + + // Check is Easy Connect handshaking or not + private boolean isGoingInitiator() { + final WifiDppInitiatorViewModel model = + ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class); + + return model.isGoingInitiator(); + } } diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java index 65be11cef4b..1770a02c8f6 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java +++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java @@ -57,7 +57,6 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements WifiNetworkConfig.Retriever, WifiDppQrCodeGeneratorFragment.OnQrCodeGeneratorFragmentAddButtonClickedListener, WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener, - WifiDppQrCodeScannerFragment.OnScanZxingWifiFormatSuccessListener, WifiDppAddDeviceFragment.OnClickChooseDifferentNetworkListener, WifiNetworkListFragment.OnChooseNetworkListener { @@ -102,7 +101,7 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements if (savedInstanceState != null) { String qrCode = savedInstanceState.getString(KEY_QR_CODE); - mWifiDppQrCode = getValidWifiDppQrCodeOrNull(qrCode); + mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode); String security = savedInstanceState.getString(KEY_WIFI_SECURITY); String ssid = savedInstanceState.getString(KEY_WIFI_SSID); @@ -148,7 +147,7 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE: String qrCode = intent.getStringExtra(Settings.EXTRA_QR_CODE); mIsTest = intent.getBooleanExtra(WifiDppUtils.EXTRA_TEST, false); - mWifiDppQrCode = getValidWifiDppQrCodeOrNull(qrCode); + mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode); final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this); if (!isDppSupported) { Log.d(TAG, "Device doesn't support Wifi DPP"); @@ -285,21 +284,6 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements fragmentTransaction.commit(); } - private WifiQrCode getValidWifiDppQrCodeOrNull(String qrCode) { - WifiQrCode wifiQrCode; - try { - wifiQrCode = new WifiQrCode(qrCode); - } catch(IllegalArgumentException e) { - return null; - } - - if (WifiQrCode.SCHEME_DPP.equals(wifiQrCode.getScheme())) { - return wifiQrCode; - } - - return null; - } - @Override public WifiNetworkConfig getWifiNetworkConfig() { return mWifiNetworkConfig; @@ -358,11 +342,6 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements showAddDeviceFragment(/* addToBackStack */ true); } - @Override - public void onScanZxingWifiFormatSuccess(WifiNetworkConfig wifiNetworkConfig) { - // Do nothing, it's impossible to be a configurator without a Wi-Fi DPP QR code - } - @Override public void onClickChooseDifferentNetwork() { showChooseSavedWifiNetworkFragment(/* addToBackStack */ true); diff --git a/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java b/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java index 143f87f943b..392b27dc46d 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java +++ b/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java @@ -17,14 +17,10 @@ package com.android.settings.wifi.dpp; import android.app.ActionBar; -import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiManager; import android.os.Bundle; -import android.provider.Settings; import android.util.Log; import androidx.fragment.app.Fragment; @@ -34,9 +30,6 @@ import androidx.fragment.app.FragmentTransaction; import com.android.settings.R; import com.android.settings.core.InstrumentedActivity; -import java.util.List; -import java.util.concurrent.Executor; - /** * To provision "this" device with specified Wi-Fi network. * @@ -44,9 +37,7 @@ import java.util.concurrent.Executor; * Wi-Fi network to be provisioned in {@code WifiDppUtils.EXTRA_WIFI_SSID}. */ public class WifiDppEnrolleeActivity extends InstrumentedActivity implements - WifiManager.ActionListener, - WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener, - WifiDppQrCodeScannerFragment.OnScanZxingWifiFormatSuccessListener { + WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener { private static final String TAG = "WifiDppEnrolleeActivity"; public static final String ACTION_ENROLLEE_QR_CODE_SCANNER = @@ -54,40 +45,6 @@ public class WifiDppEnrolleeActivity extends InstrumentedActivity implements private FragmentManager mFragmentManager; - private class EasyConnectStatusCallback extends android.net.wifi.EasyConnectStatusCallback { - @Override - public void onEnrolleeSuccess(int newNetworkId) { - // Connect to the new network. - final WifiManager wifiManager = getSystemService(WifiManager.class); - final List wifiConfigs = - wifiManager.getPrivilegedConfiguredNetworks(); - for (WifiConfiguration wifiConfig : wifiConfigs) { - if (wifiConfig.networkId == newNetworkId) { - wifiManager.connect(wifiConfig, WifiDppEnrolleeActivity.this); - return; - } - } - Log.e(TAG, "Invalid networkId " + newNetworkId); - WifiDppEnrolleeActivity.this.onFailure(WifiManager.ERROR_AUTHENTICATING); - } - - @Override - public void onConfiguratorSuccess(int code) { - // Do nothing - } - - @Override - public void onFailure(int code) { - //TODO(b/122429170): Show DPP enrollee error state UI - Log.d(TAG, "EasyConnectStatusCallback.onFailure " + code); - } - - @Override - public void onProgress(int code) { - // Do nothing - } - } - @Override public int getMetricsCategory() { return SettingsEnums.SETTINGS_WIFI_DPP_ENROLLEE; @@ -158,30 +115,6 @@ public class WifiDppEnrolleeActivity extends InstrumentedActivity implements @Override public void onScanWifiDppSuccess(WifiQrCode wifiQrCode) { - final WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); - wifiManager.startEasyConnectAsEnrolleeInitiator(wifiQrCode.getQrCode(), getMainExecutor(), - new EasyConnectStatusCallback()); - } - - @Override - public void onScanZxingWifiFormatSuccess(WifiNetworkConfig wifiNetworkConfig) { - wifiNetworkConfig.connect(/* context */ this, /* listener */ this); - } - - @Override - public void onSuccess() { - startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); - setResult(Activity.RESULT_OK); - finish(); - } - - @Override - public void onFailure(int reason) { - Log.d(TAG, "Wi-Fi connect onFailure reason - " + reason); - - final Fragment fragment = mFragmentManager.findFragmentById(R.id.fragment_container); - if (fragment instanceof WifiDppQrCodeScannerFragment) { - ((WifiDppQrCodeScannerFragment)fragment).showErrorMessage(true); - } + // Do nothing } } diff --git a/src/com/android/settings/wifi/dpp/WifiDppInitiatorViewModel.java b/src/com/android/settings/wifi/dpp/WifiDppInitiatorViewModel.java new file mode 100644 index 00000000000..24e5ebeb24c --- /dev/null +++ b/src/com/android/settings/wifi/dpp/WifiDppInitiatorViewModel.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019 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.dpp; + +import android.app.Application; +import android.net.wifi.EasyConnectStatusCallback; +import android.net.wifi.WifiManager; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.AndroidViewModel; + +public class WifiDppInitiatorViewModel extends AndroidViewModel { + private MutableLiveData mEnrolleeSuccessNetworkId; + private MutableLiveData mStatusCode; + private boolean mIsGoingInitiator; + + public WifiDppInitiatorViewModel(Application application) { + super(application); + } + + public MutableLiveData getEnrolleeSuccessNetworkId() { + if (mEnrolleeSuccessNetworkId == null) { + mEnrolleeSuccessNetworkId = new MutableLiveData<>(); + } + + return mEnrolleeSuccessNetworkId; + } + + public MutableLiveData getStatusCode() { + if (mStatusCode == null) { + mStatusCode = new MutableLiveData<>(); + } + + return mStatusCode; + } + + public boolean isGoingInitiator() { + return mIsGoingInitiator; + } + + public void startEasyConnectAsConfiguratorInitiator(String qrCode, int networkId) { + mIsGoingInitiator = true; + final WifiManager wifiManager = getApplication().getSystemService(WifiManager.class); + + wifiManager.startEasyConnectAsConfiguratorInitiator(qrCode, networkId, + WifiManager.EASY_CONNECT_NETWORK_ROLE_STA, getApplication().getMainExecutor(), + new EasyConnectDelegateCallback()); + } + + public void startEasyConnectAsEnrolleeInitiator(String qrCode) { + mIsGoingInitiator = true; + final WifiManager wifiManager = getApplication().getSystemService(WifiManager.class); + + wifiManager.startEasyConnectAsEnrolleeInitiator(qrCode, getApplication().getMainExecutor(), + new EasyConnectDelegateCallback()); + } + + private class EasyConnectDelegateCallback extends EasyConnectStatusCallback { + @Override + public void onEnrolleeSuccess(int newNetworkId) { + mIsGoingInitiator = false; + mEnrolleeSuccessNetworkId.setValue(newNetworkId); + } + + @Override + public void onConfiguratorSuccess(int code) { + mIsGoingInitiator = false; + mStatusCode.setValue(WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS); + } + + @Override + public void onFailure(int code) { + mIsGoingInitiator = false; + mStatusCode.setValue(code); + } + + @Override + public void onProgress(int code) { + // Do nothing + } + } +} diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java index 9524fee9051..fe4e8deffa3 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java +++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java @@ -17,15 +17,23 @@ package com.android.settings.wifi.dpp; import android.app.ActionBar; +import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.Intent; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.net.wifi.EasyConnectStatusCallback; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.Settings; import android.text.TextUtils; +import android.util.Log; import android.util.Size; import android.view.LayoutInflater; import android.view.Menu; @@ -36,13 +44,18 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.lifecycle.ViewModelProviders; + import com.android.settings.R; import com.android.settings.wifi.qrcode.QrCamera; import com.android.settings.wifi.qrcode.QrDecorateView; +import java.util.List; + public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implements SurfaceTextureListener, - QrCamera.ScannerCallback { + QrCamera.ScannerCallback, + WifiManager.ActionListener { private static final String TAG = "WifiDppQrCodeScannerFragment"; /** Message sent to hide error message */ @@ -57,12 +70,12 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl /** Message sent to manipulate ZXing Wi-Fi QR code */ private static final int MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS = 4; - private static final long SHOW_ERROR_MESSAGE_INTERVAL = 2000; + private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000; private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000; // Key for Bundle usage - private static final String KEY_PUBLIC_URI = "key_public_uri"; private static final String KEY_IS_CONFIGURATOR_MODE = "key_is_configurator_mode"; + private static final String KEY_LATEST_ERROR_CODE = "key_latest_error_code"; private QrCamera mCamera; private TextureView mTextureView; @@ -78,13 +91,41 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl /** QR code data scanned by camera */ private WifiQrCode mWifiQrCode; + private int mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_FAILURE_NONE; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { mIsConfiguratorMode = savedInstanceState.getBoolean(KEY_IS_CONFIGURATOR_MODE); + mLatestStatusCode = savedInstanceState.getInt(KEY_LATEST_ERROR_CODE); } + + final WifiDppInitiatorViewModel model = + ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class); + + model.getEnrolleeSuccessNetworkId().observe(this, networkId -> { + // After configuration change, observe callback will be triggered, + // do nothing for this case if a handshake does not end + if (model.isGoingInitiator()) { + return; + } + + new EasyConnectEnrolleeStatusCallback().onEnrolleeSuccess(networkId.intValue()); + }); + + model.getStatusCode().observe(this, statusCode -> { + // After configuration change, observe callback will be triggered, + // do nothing for this case if a handshake does not end + if (model.isGoingInitiator()) { + return; + } + + int code = statusCode.intValue(); + Log.d(TAG, "Easy connect enrollee callback onFailure " + code); + new EasyConnectEnrolleeStatusCallback().onFailure(code); + }); } @Override @@ -102,12 +143,6 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl } OnScanWifiDppSuccessListener mScanWifiDppSuccessListener; - // Container Activity must implement this interface - public interface OnScanZxingWifiFormatSuccessListener { - public void onScanZxingWifiFormatSuccess(WifiNetworkConfig wifiNetworkConfig); - } - OnScanZxingWifiFormatSuccessListener mScanZxingWifiFormatSuccessListener; - /** * Configurator container activity of the fragment should create instance with this constructor. */ @@ -144,13 +179,11 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl super.onAttach(context); mScanWifiDppSuccessListener = (OnScanWifiDppSuccessListener) context; - mScanZxingWifiFormatSuccessListener = (OnScanZxingWifiFormatSuccessListener) context; } @Override public void onDetach() { mScanWifiDppSuccessListener = null; - mScanZxingWifiFormatSuccessListener = null; super.onDetach(); } @@ -317,7 +350,14 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl // Check if the camera has already created. if (mCamera == null) { mCamera = new QrCamera(getContext(), this); - mCamera.start(surface); + + if (isGoingInitiator()) { + if (mDecorateView != null) { + mDecorateView.setFocused(true); + } + } else { + mCamera.start(surface); + } } } @@ -328,14 +368,13 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl } } - public void showErrorMessage(boolean show) { - mErrorMessage.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + public void showErrorMessage(String message) { + mErrorMessage.setVisibility(View.VISIBLE); + mErrorMessage.setText(message); - if (show) { - mHandler.removeMessages(MESSAGE_HIDE_ERROR_MESSAGE); - mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE, - SHOW_ERROR_MESSAGE_INTERVAL); - } + mHandler.removeMessages(MESSAGE_HIDE_ERROR_MESSAGE); + mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE, + SHOW_ERROR_MESSAGE_INTERVAL); } private final Handler mHandler = new Handler() { @@ -343,26 +382,32 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_HIDE_ERROR_MESSAGE: - showErrorMessage(false); + mErrorMessage.setVisibility(View.INVISIBLE); break; case MESSAGE_SHOW_ERROR_MESSAGE: - showErrorMessage(true); + showErrorMessage(getString(R.string.wifi_dpp_could_not_detect_valid_qr_code)); break; case MESSAGE_SCAN_WIFI_DPP_SUCCESS: + mErrorMessage.setVisibility(View.INVISIBLE); + if (mScanWifiDppSuccessListener == null) { return; } mScanWifiDppSuccessListener.onScanWifiDppSuccess((WifiQrCode)msg.obj); + + if (!mIsConfiguratorMode) { + startWifiDppEnrolleeInitiator((WifiQrCode)msg.obj); + } break; case MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS: - if (mScanZxingWifiFormatSuccessListener == null) { - return; - } - mScanZxingWifiFormatSuccessListener.onScanZxingWifiFormatSuccess( - (WifiNetworkConfig)msg.obj); + mErrorMessage.setVisibility(View.INVISIBLE); + + final WifiNetworkConfig wifiNetworkConfig = (WifiNetworkConfig)msg.obj; + wifiNetworkConfig.connect(getContext(), + /* listener */ WifiDppQrCodeScannerFragment.this); break; default: @@ -374,7 +419,129 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl @Override public void onSaveInstanceState(Bundle outState) { outState.putBoolean(KEY_IS_CONFIGURATOR_MODE, mIsConfiguratorMode); + outState.putInt(KEY_LATEST_ERROR_CODE, mLatestStatusCode); super.onSaveInstanceState(outState); } + + private class EasyConnectEnrolleeStatusCallback extends EasyConnectStatusCallback { + @Override + public void onEnrolleeSuccess(int newNetworkId) { + + // Connect to the new network. + final WifiManager wifiManager = getContext().getSystemService(WifiManager.class); + final List wifiConfigs = + wifiManager.getPrivilegedConfiguredNetworks(); + for (WifiConfiguration wifiConfig : wifiConfigs) { + if (wifiConfig.networkId == newNetworkId) { + mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS; + wifiManager.connect(wifiConfig, WifiDppQrCodeScannerFragment.this); + return; + } + } + + Log.e(TAG, "Invalid networkId " + newNetworkId); + mLatestStatusCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC; + showErrorMessage(getString(R.string.wifi_dpp_check_connection_try_again)); + } + + @Override + public void onConfiguratorSuccess(int code) { + // Do nothing + } + + @Override + public void onFailure(int code) { + Log.d(TAG, "EasyConnectEnrolleeStatusCallback.onFailure " + code); + + switch (code) { + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI: + showErrorMessage(getString(R.string.wifi_dpp_could_not_detect_valid_qr_code)); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION: + showErrorMessage( + getString(R.string.wifi_dpp_failure_authentication_or_configuration)); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE: + showErrorMessage(getString(R.string.wifi_dpp_failure_not_compatible)); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION: + showErrorMessage( + getString(R.string.wifi_dpp_failure_authentication_or_configuration)); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY: + if (code == mLatestStatusCode) { + throw(new IllegalStateException("stopEasyConnectSession and try again for" + + "EASY_CONNECT_EVENT_FAILURE_BUSY but still failed")); + } + + mLatestStatusCode = code; + final WifiManager wifiManager = + getContext().getSystemService(WifiManager.class); + wifiManager.stopEasyConnectSession(); + startWifiDppEnrolleeInitiator(mWifiQrCode); + return; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT: + showErrorMessage(getString(R.string.wifi_dpp_failure_timeout)); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC: + showErrorMessage(getString(R.string.wifi_dpp_failure_generic)); + break; + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED: + throw(new IllegalStateException("EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED" + + " should be a configurator only error")); + + case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK: + throw(new IllegalStateException("EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK" + + " should be a configurator only error")); + + default: + throw(new IllegalStateException("Unexpected Wi-Fi DPP error")); + } + + mLatestStatusCode = code; + } + + @Override + public void onProgress(int code) { + // Do nothing + } + } + + private void startWifiDppEnrolleeInitiator(WifiQrCode wifiQrCode) { + final WifiDppInitiatorViewModel model = + ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class); + + model.startEasyConnectAsEnrolleeInitiator(wifiQrCode.getQrCode()); + } + + @Override + public void onSuccess() { + startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); + final Activity hostActivity = getActivity(); + hostActivity.setResult(Activity.RESULT_OK); + hostActivity.finish(); + } + + @Override + public void onFailure(int reason) { + Log.d(TAG, "Wi-Fi connect onFailure reason - " + reason); + + showErrorMessage(getString(R.string.wifi_dpp_check_connection_try_again)); + } + + // Check is Easy Connect handshaking or not + private boolean isGoingInitiator() { + final WifiDppInitiatorViewModel model = + ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class); + + return model.isGoingInitiator(); + } } diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java index 8b333e22dfa..24cd1d75a63 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java +++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java @@ -74,6 +74,16 @@ public class WifiDppUtils { * indicate test mode UI should be shown. Test UI does not make API calls. Value is a boolean.*/ public static final String EXTRA_TEST = "test"; + /** + * Default status code for Easy Connect + */ + public static final int EASY_CONNECT_EVENT_FAILURE_NONE = 0; + + /** + * Success status code for Easy Connect. + */ + public static final int EASY_CONNECT_EVENT_SUCCESS = 1; + /** * Returns whether the device support WiFi DPP. */ diff --git a/src/com/android/settings/wifi/dpp/WifiQrCode.java b/src/com/android/settings/wifi/dpp/WifiQrCode.java index a08fb4460d8..10971cfd3a6 100644 --- a/src/com/android/settings/wifi/dpp/WifiQrCode.java +++ b/src/com/android/settings/wifi/dpp/WifiQrCode.java @@ -228,4 +228,19 @@ public class WifiQrCode { return new WifiNetworkConfig(mWifiNetworkConfig); } + + public static WifiQrCode getValidWifiDppQrCodeOrNull(String qrCode) { + WifiQrCode wifiQrCode; + try { + wifiQrCode = new WifiQrCode(qrCode); + } catch(IllegalArgumentException e) { + return null; + } + + if (SCHEME_DPP.equals(wifiQrCode.getScheme())) { + return wifiQrCode; + } + + return null; + } } diff --git a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java index 76df7643287..a42517751de 100644 --- a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java +++ b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java @@ -109,14 +109,6 @@ public class WifiDppConfiguratorActivityTest { .OnScanWifiDppSuccessListener).isEqualTo(true); } - @Test - public void testActivity_shouldImplementsOnScanZxingWifiFormatSuccessCallback() { - WifiDppConfiguratorActivity activity = mActivityRule.getActivity(); - - assertThat(activity instanceof WifiDppQrCodeScannerFragment - .OnScanZxingWifiFormatSuccessListener).isEqualTo(true); - } - @Test public void testActivity_shouldImplementsOnClickChooseDifferentNetworkCallback() { WifiDppConfiguratorActivity activity = mActivityRule.getActivity(); diff --git a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java index 283da1358b8..aef4767ed08 100644 --- a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java +++ b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java @@ -38,12 +38,4 @@ public class WifiDppEnrolleeActivityTest { assertThat(activity instanceof WifiDppQrCodeScannerFragment .OnScanWifiDppSuccessListener).isEqualTo(true); } - - @Test - public void testActivity_shouldImplementsOnScanZxingWifiFormatSuccessCallback() { - WifiDppEnrolleeActivity activity = mActivityRule.getActivity(); - - assertThat(activity instanceof WifiDppQrCodeScannerFragment - .OnScanZxingWifiFormatSuccessListener).isEqualTo(true); - } }