Merge changes from topic "Wi-Fi DPP error code handling"

* changes:
  Fix camera stop forever after error occurs while sharing or adding network
  Handle different error codes in DPP
This commit is contained in:
TreeHugger Robot
2019-02-20 12:45:26 +00:00
committed by Android (Google) Code Review
12 changed files with 555 additions and 176 deletions

View File

@@ -46,9 +46,14 @@
android:layout_height="match_parent"/>
</com.android.settings.wifi.qrcode.QrPreviewLayout>
<TextView android:id="@+id/error_message"
<TextView
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginTop="8dp"
android:visibility="invisible"
android:textColor="?android:attr/colorError"/>
</LinearLayout>

View File

@@ -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"/>

View File

@@ -2124,7 +2124,19 @@
<!-- Hint for the user to use another device to scan QR code on screen to join Wi-Fi [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_scan_qr_code_with_another_device">Scan this QR code with another device to join \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d</string>
<!-- Hint for QR code detection [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_could_not_detect_valid_qr_code">Couldn\u2019t read QR code</string>
<string name="wifi_dpp_could_not_detect_valid_qr_code">Couldn\u2019t read QR code. Re-center code and try again</string>
<!-- Hint for Wi-Fi DPP handshake failure [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_failure_authentication_or_configuration">Try again. If the issue continues, contact the device manufacturer</string>
<!-- Hint for Wi-Fi DPP handshake failure [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_failure_not_compatible">Something went wrong</string>
<!-- Hint for Wi-Fi DPP handshake failure [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_failure_timeout">Make sure the device has been plugged in, charged, and turned on</string>
<!-- Hint for Wi-Fi DPP handshake failure [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_failure_generic">Make sure the device has been plugged in, charged, and turned on. If the issue continues, contact the device manufacturer</string>
<!-- Hint for Wi-Fi DPP handshake failure [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_failure_not_supported">Adding \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d isn\u2019t supported by this device</string>
<!-- Hint for Wi-Fi connection fail [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_check_connection_try_again">Check connection and try again</string>
<!-- Title for the fragment choose network [CHAR LIMIT=50] -->
<string name="wifi_dpp_choose_network">Choose network</string>
<!-- Hint for the user to center another device's QR code in the below camera window [CHAR LIMIT=NONE] -->

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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<WifiConfiguration> 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
}
}

View File

@@ -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<Integer> mEnrolleeSuccessNetworkId;
private MutableLiveData<Integer> mStatusCode;
private boolean mIsGoingInitiator;
public WifiDppInitiatorViewModel(Application application) {
super(application);
}
public MutableLiveData<Integer> getEnrolleeSuccessNetworkId() {
if (mEnrolleeSuccessNetworkId == null) {
mEnrolleeSuccessNetworkId = new MutableLiveData<>();
}
return mEnrolleeSuccessNetworkId;
}
public MutableLiveData<Integer> 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
}
}
}

View File

@@ -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();
}
@@ -289,7 +322,9 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
}
private void handleWifiDpp() {
destroyCamera();
if (mCamera != null) {
mCamera.stop();
}
mDecorateView.setFocused(true);
Message message = mHandler.obtainMessage(MESSAGE_SCAN_WIFI_DPP_SUCCESS);
@@ -299,7 +334,9 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
}
private void handleZxingWifiFormat() {
destroyCamera();
if (mCamera != null) {
mCamera.stop();
}
mDecorateView.setFocused(true);
Message message = mHandler.obtainMessage(MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS);
@@ -317,7 +354,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 +372,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 +386,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 +423,149 @@ 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<WifiConfiguration> 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));
restartCamera();
}
@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;
restartCamera();
}
@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));
restartCamera();
}
// Check is Easy Connect handshaking or not
private boolean isGoingInitiator() {
final WifiDppInitiatorViewModel model =
ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class);
return model.isGoingInitiator();
}
/**
* To resume camera decoding task after handshake fail or Wi-Fi connection fail.
*/
private void restartCamera() {
if (mCamera == null) {
Log.d(TAG, "mCamera is not available for restarting camera");
return;
}
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture == null) {
throw new IllegalStateException("SurfaceTexture is not ready for restarting camera");
}
mCamera.start(surfaceTexture);
}
}

View File

@@ -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.
*/

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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);
}
}