From da21128568e29a6c2992b1b38cbb2db9ec36892f Mon Sep 17 00:00:00 2001 From: Les Lee Date: Wed, 24 Jul 2024 20:32:54 +0000 Subject: [PATCH] wifi: fix ADB URI doesn't work Using old WifiQrCode parsing for ADB Uri. Flag: EXEMPT bugfix Bug: 355088188 Test: Manual test & TH Change-Id: I54a12b03ed9be6dc49fb957df0f1f7b31647810d --- .../development/AdbQrcodeScannerFragment.java | 19 +- .../android/settings/wifi/dpp/AdbQrCode.java | 182 ++++++++++++++++-- .../settings/wifi/dpp/AdbQrCodeTest.java | 65 +++++++ 3 files changed, 241 insertions(+), 25 deletions(-) create mode 100644 tests/unit/src/com/android/settings/wifi/dpp/AdbQrCodeTest.java diff --git a/src/com/android/settings/development/AdbQrcodeScannerFragment.java b/src/com/android/settings/development/AdbQrcodeScannerFragment.java index 1d384544447..ca44747f163 100644 --- a/src/com/android/settings/development/AdbQrcodeScannerFragment.java +++ b/src/com/android/settings/development/AdbQrcodeScannerFragment.java @@ -16,7 +16,6 @@ package com.android.settings.development; -import android.annotation.Nullable; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; @@ -27,7 +26,6 @@ import android.debug.IAdbManager; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.net.wifi.WifiConfiguration; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -49,6 +47,7 @@ import com.android.settings.R; import com.android.settings.SetupWizardUtils; import com.android.settings.wifi.dpp.AdbQrCode; import com.android.settings.wifi.dpp.WifiDppQrCodeBaseFragment; +import com.android.settings.wifi.dpp.WifiNetworkConfig; import com.android.settingslib.qrcode.QrCamera; import com.android.settingslib.qrcode.QrDecorateView; @@ -82,8 +81,7 @@ public class AdbQrcodeScannerFragment extends WifiDppQrCodeBaseFragment implemen /** QR code data scanned by camera */ private AdbQrCode mAdbQrCode; - @Nullable - private WifiConfiguration mAdbConfig; + private WifiNetworkConfig mAdbConfig; private IAdbManager mAdbManager; @@ -289,16 +287,13 @@ public class AdbQrcodeScannerFragment extends WifiDppQrCodeBaseFragment implemen AdbQrCode.triggerVibrationForQrCodeRecognition(getContext()); mVerifyingTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); try { - if (mAdbConfig != null) { - mAdbManager.enablePairingByQrCode(mAdbConfig.SSID, - mAdbConfig.preSharedKey); - return; - } + mAdbManager.enablePairingByQrCode(mAdbConfig.getSsid(), + mAdbConfig.getPreSharedKey()); } catch (RemoteException e) { - Log.e(TAG, "Unable to enable QR code pairing" + e); + Log.e(TAG, "Unable to enable QR code pairing"); + getActivity().setResult(Activity.RESULT_CANCELED); + getActivity().finish(); } - getActivity().setResult(Activity.RESULT_CANCELED); - getActivity().finish(); } @Override diff --git a/src/com/android/settings/wifi/dpp/AdbQrCode.java b/src/com/android/settings/wifi/dpp/AdbQrCode.java index 2d830b2820a..8a578ea4d3d 100644 --- a/src/com/android/settings/wifi/dpp/AdbQrCode.java +++ b/src/com/android/settings/wifi/dpp/AdbQrCode.java @@ -16,11 +16,14 @@ package com.android.settings.wifi.dpp; import android.content.Context; -import android.net.wifi.UriParserResults; import android.net.wifi.WifiConfiguration; import android.text.TextUtils; -import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; /** * Extension of WifiQrCode to support ADB QR code format. @@ -28,34 +31,74 @@ import androidx.annotation.NonNull; * * WIFI:T:ADB;S:myname;P:mypassword;; */ -public class AdbQrCode extends WifiQrCode { +public class AdbQrCode { static final String SECURITY_ADB = "ADB"; + static final String SCHEME_DPP = "DPP"; + static final String SCHEME_ZXING_WIFI_NETWORK_CONFIG = "WIFI"; + static final String PREFIX_DPP = "DPP:"; + static final String PREFIX_ZXING_WIFI_NETWORK_CONFIG = "WIFI:"; - private WifiConfiguration mAdbConfig; + static final String PREFIX_DPP_PUBLIC_KEY = "K:"; + static final String PREFIX_DPP_INFORMATION = "I:"; + + static final String PREFIX_ZXING_SECURITY = "T:"; + static final String PREFIX_ZXING_SSID = "S:"; + static final String PREFIX_ZXING_PASSWORD = "P:"; + static final String PREFIX_ZXING_HIDDEN_SSID = "H:"; + static final String DELIMITER_QR_CODE = ";"; + // Ignores password if security is SECURITY_NO_PASSWORD or absent + static final String SECURITY_NO_PASSWORD = "nopass"; //open network or OWE + static final String SECURITY_WEP = "WEP"; + static final String SECURITY_WPA_PSK = "WPA"; + static final String SECURITY_SAE = "SAE"; + private String mQrCode; + /** + * SCHEME_DPP for standard Wi-Fi device provision protocol; SCHEME_ZXING_WIFI_NETWORK_CONFIG + * for ZXing reader library' Wi-Fi Network config format + */ + private String mScheme; + // Data from parsed Wi-Fi DPP QR code + private String mPublicKey; + private String mInformation; + // Data from parsed ZXing reader library's Wi-Fi Network config format + private WifiNetworkConfig mAdbConfig; public AdbQrCode(String qrCode) throws IllegalArgumentException { - super(qrCode); + if (TextUtils.isEmpty(qrCode)) { + throw new IllegalArgumentException("Empty QR code"); + } + + mQrCode = qrCode; + if (qrCode.startsWith(PREFIX_DPP)) { + mScheme = SCHEME_DPP; + parseWifiDppQrCode(qrCode); + } else if (qrCode.startsWith(PREFIX_ZXING_WIFI_NETWORK_CONFIG)) { + mScheme = SCHEME_ZXING_WIFI_NETWORK_CONFIG; + parseZxingWifiQrCode(qrCode); + } else { + throw new IllegalArgumentException("Invalid scheme"); + } // Only accept the zxing format. - if (getScheme() != UriParserResults.URI_SCHEME_ZXING_WIFI_NETWORK_CONFIG) { + if (!SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(getScheme())) { throw new IllegalArgumentException("DPP format not supported for ADB QR code"); } - mAdbConfig = getWifiConfiguration(); + mAdbConfig = getWifiNetworkConfig(); - if (mAdbConfig == null) { - throw new IllegalArgumentException("Null config when parsing ADB QR code"); + if (!SECURITY_ADB.equals(mAdbConfig.getSecurity())) { + throw new IllegalArgumentException("Invalid security type"); } - if (TextUtils.isEmpty(mAdbConfig.SSID)) { + + if (TextUtils.isEmpty(mAdbConfig.getSsid())) { throw new IllegalArgumentException("Empty service name"); } - if (TextUtils.isEmpty(mAdbConfig.preSharedKey)) { + if (TextUtils.isEmpty(mAdbConfig.getPreSharedKey())) { throw new IllegalArgumentException("Empty password"); } } - @NonNull - public WifiConfiguration getAdbNetworkConfig() { + public WifiNetworkConfig getAdbNetworkConfig() { return mAdbConfig; } @@ -67,4 +110,117 @@ public class AdbQrCode extends WifiQrCode { public static void triggerVibrationForQrCodeRecognition(Context context) { WifiDppUtils.triggerVibrationForQrCodeRecognition(context); } + + /** Parses Wi-Fi DPP QR code string */ + private void parseWifiDppQrCode(String qrCode) throws IllegalArgumentException { + List keyValueList = getKeyValueList(qrCode, PREFIX_DPP, DELIMITER_QR_CODE); + String publicKey = getValueOrNull(keyValueList, PREFIX_DPP_PUBLIC_KEY); + if (TextUtils.isEmpty(publicKey)) { + throw new IllegalArgumentException("Invalid format"); + } + mPublicKey = publicKey; + mInformation = getValueOrNull(keyValueList, PREFIX_DPP_INFORMATION); + } + + /** Parses ZXing reader library's Wi-Fi Network config format */ + private void parseZxingWifiQrCode(String qrCode) throws IllegalArgumentException { + List keyValueList = getKeyValueList(qrCode, PREFIX_ZXING_WIFI_NETWORK_CONFIG, + DELIMITER_QR_CODE); + String security = getValueOrNull(keyValueList, PREFIX_ZXING_SECURITY); + String ssid = getValueOrNull(keyValueList, PREFIX_ZXING_SSID); + String password = getValueOrNull(keyValueList, PREFIX_ZXING_PASSWORD); + String hiddenSsidString = getValueOrNull(keyValueList, PREFIX_ZXING_HIDDEN_SSID); + boolean hiddenSsid = "true".equalsIgnoreCase(hiddenSsidString); + //"\", ";", "," and ":" are escaped with a backslash "\", should remove at first + security = removeBackSlash(security); + ssid = removeBackSlash(ssid); + password = removeBackSlash(password); + mAdbConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid, password, + hiddenSsid, WifiConfiguration.INVALID_NETWORK_ID, /* isHotspot */ false); + if (mAdbConfig == null) { + throw new IllegalArgumentException("Invalid format"); + } + } + + /** + * Splits key/value pairs from qrCode + * + * @param qrCode the QR code raw string + * @param prefixQrCode the string before all key/value pairs in qrCode + * @param delimiter the string to split key/value pairs, can't contain a backslash + * @return a list contains string of key/value (e.g. K:key1) + */ + private List getKeyValueList(String qrCode, String prefixQrCode, + String delimiter) { + String keyValueString = qrCode.substring(prefixQrCode.length()); + // Should not treat \delimiter as a delimiter + String regex = "(? keyValueList, String prefix) { + for (String keyValue : keyValueList) { + String strippedKeyValue = keyValue.stripLeading(); + if (strippedKeyValue.startsWith(prefix)) { + return strippedKeyValue.substring(prefix.length()); + } + } + return null; + } + + @VisibleForTesting + String removeBackSlash(String input) { + if (input == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + boolean backSlash = false; + for (char ch : input.toCharArray()) { + if (ch != '\\') { + sb.append(ch); + backSlash = false; + } else { + if (backSlash) { + sb.append(ch); + backSlash = false; + continue; + } + backSlash = true; + } + } + return sb.toString(); + } + + String getQrCode() { + return mQrCode; + } + + /** + * Uses to check type of QR code + * + * SCHEME_DPP for standard Wi-Fi device provision protocol; SCHEME_ZXING_WIFI_NETWORK_CONFIG + * for ZXing reader library' Wi-Fi Network config format + */ + public String getScheme() { + return mScheme; + } + + /** Available when {@code getScheme()} returns SCHEME_DPP */ + @VisibleForTesting + String getPublicKey() { + return mPublicKey; + } + + /** May be available when {@code getScheme()} returns SCHEME_DPP */ + public String getInformation() { + return mInformation; + } + + /** Available when {@code getScheme()} returns SCHEME_ZXING_WIFI_NETWORK_CONFIG */ + WifiNetworkConfig getWifiNetworkConfig() { + if (mAdbConfig == null) { + return null; + } + return new WifiNetworkConfig(mAdbConfig); + } } diff --git a/tests/unit/src/com/android/settings/wifi/dpp/AdbQrCodeTest.java b/tests/unit/src/com/android/settings/wifi/dpp/AdbQrCodeTest.java new file mode 100644 index 00000000000..44f6c56aa18 --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/dpp/AdbQrCodeTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 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 static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class AdbQrCodeTest { + @Test + public void testZxParsing_validCode() { + WifiNetworkConfig config = new AdbQrCode( + "WIFI:S:reallyLONGone;T:ADB;P:somepasswo#%^**123rd").getWifiNetworkConfig(); + assertThat(config.getSsid()).isEqualTo("reallyLONGone"); + assertThat(config.getSecurity()).isEqualTo("ADB"); + assertThat(config.getPreSharedKey()).isEqualTo("somepasswo#%^**123rd"); + + config = new AdbQrCode("WIFI:S:anotherone;T:ADB;P:3#=3j9asicla").getWifiNetworkConfig(); + assertThat(config.getSsid()).isEqualTo("anotherone"); + assertThat(config.getSecurity()).isEqualTo("ADB"); + assertThat(config.getPreSharedKey()).isEqualTo("3#=3j9asicla"); + + config = new AdbQrCode("WIFI:S:xx;T:ADB;P:a").getWifiNetworkConfig(); + assertThat(config.getSsid()).isEqualTo("xx"); + assertThat(config.getSecurity()).isEqualTo("ADB"); + assertThat(config.getPreSharedKey()).isEqualTo("a"); + } + + @Test + public void testZxParsing_invalidCodeButShouldWork() { + WifiNetworkConfig config = new AdbQrCode( + "WIFI:S:reallyLONGone;T:ADB; P:somepassword").getWifiNetworkConfig(); + assertThat(config.getSsid()).isEqualTo("reallyLONGone"); + assertThat(config.getSecurity()).isEqualTo("ADB"); + assertThat(config.getPreSharedKey()).isEqualTo("somepassword"); + + config = new AdbQrCode("WIFI: S:anotherone;T:ADB;P:abcdefghihklmn").getWifiNetworkConfig(); + assertThat(config.getSsid()).isEqualTo("anotherone"); + assertThat(config.getSecurity()).isEqualTo("ADB"); + assertThat(config.getPreSharedKey()).isEqualTo("abcdefghihklmn"); + + config = new AdbQrCode("WIFI: S:xx; T:ADB; P:a").getWifiNetworkConfig(); + assertThat(config.getSsid()).isEqualTo("xx"); + assertThat(config.getSecurity()).isEqualTo("ADB"); + assertThat(config.getPreSharedKey()).isEqualTo("a"); + } +} +