From 9f9afe56e844f75ffcd698e3ecae276a8974cd99 Mon Sep 17 00:00:00 2001 From: Nick Lambourne Date: Thu, 13 Jul 2023 14:39:36 +0100 Subject: [PATCH 1/2] Initial second chance fix --- app/build.gradle | 4 ++-- app/src/main/AndroidManifest.xml | 4 ++++ cloudsdk/build.gradle | 4 ++-- commonui/build.gradle | 4 ++-- devicesetup/build.gradle | 4 ++-- .../apconnector/ApConnectorApi21.kt | 20 +++---------------- .../apconnector/ApConnectorApi29.kt | 2 ++ .../android/sdk/utils/WifiFacade.java | 11 ++++++++-- ecjpake4j/build.gradle | 4 ++-- firmwareprotos/build.gradle | 4 ++-- mesh/build.gradle | 4 ++-- meshui/build.gradle | 4 ++-- sdk_example_app/build.gradle | 4 ++-- sdk_example_app/src/main/AndroidManifest.xml | 6 +++++- setup_exampleapp/build.gradle | 4 ++-- setup_exampleapp/src/main/AndroidManifest.xml | 2 ++ setup_testapp/build.gradle | 4 ++-- setup_testapp/src/main/AndroidManifest.xml | 1 + .../src/main/res/layout/activity_main.xml | 4 ++-- 19 files changed, 50 insertions(+), 44 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c78318d3..f0589805 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,12 +13,12 @@ apply plugin: "androidx.navigation.safeargs.kotlin" android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { applicationId 'io.particle.android.app' minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 // scheme for these version numbers: EPOCH MAJOR MINOR PATCH BUILD versionCode 1_03_01_02_01 versionName "3.1.2" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 46fa8870..231ca038 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,6 +30,7 @@ @@ -42,6 +43,7 @@ @@ -49,6 +51,7 @@ android:name="io.particle.android.sdk.ui.devicelist.DeviceListActivity" android:label="@string/title_device_list" android:screenOrientation="portrait" + android:exported="true" android:theme="@style/AppTheme.NoActionBar"> @@ -67,6 +70,7 @@ android:label="@string/title_inspector_detail" android:parentActivityName="io.particle.android.sdk.ui.devicelist.DeviceListActivity" android:screenOrientation="portrait" + android:exported="true" android:theme="@style/TinkerSetup"> onWifiChangeBroadcastReceived(intent, config) } - val useMoreComplexConnectionProcess = VERSION.SDK_INT < 18 // we don't need this for its atomicity, we just need it as a 'final' reference to an @@ -64,7 +62,7 @@ class ApConnectorApi21( // wonkiness I ran into when trying to do every one of these steps one right after // the other on the same thread. val alreadyConfiguredId = wifiFacade.getIdForConfiguredNetwork(configSSID) - if (alreadyConfiguredId != -1 && !useMoreComplexConnectionProcess) { + if (alreadyConfiguredId != -1) { // For some unexplained (and probably sad-trombone-y) reason, if the AP specified was // already configured and had been connected to in the past, it will often get to // the "CONNECTING" event, but just before firing the "CONNECTED" event, the @@ -82,7 +80,7 @@ class ApConnectorApi21( } } } - if (alreadyConfiguredId == -1 || !useMoreComplexConnectionProcess) { + if (alreadyConfiguredId == -1 ) { setupRunnables.add { log.d("Adding network $configSSID") networkID.set(wifiFacade.addNetwork(config)) @@ -100,21 +98,9 @@ class ApConnectorApi21( } } } - if (useMoreComplexConnectionProcess) { - setupRunnables.add { - log.d("Disconnecting from networks; reconnecting momentarily.") - wifiFacade.disconnect() - } - } setupRunnables.add { log.i("Enabling network " + configSSID + " with network ID " + networkID.get()) - wifiFacade.enableNetwork(networkID.get(), !useMoreComplexConnectionProcess) - } - if (useMoreComplexConnectionProcess) { - setupRunnables.add { - log.d("Disconnecting from networks; reconnecting momentarily.") - wifiFacade.reconnect() - } + wifiFacade.enableNetwork(networkID.get(), true) } val currentlyConnectedSSID = wifiFacade.currentlyConnectedSSID softAPConfigRemover.onWifiNetworkDisabled(currentlyConnectedSSID) diff --git a/devicesetup/src/main/java/io/particle/android/sdk/devicesetup/apconnector/ApConnectorApi29.kt b/devicesetup/src/main/java/io/particle/android/sdk/devicesetup/apconnector/ApConnectorApi29.kt index 0c87e57d..5a82c3ba 100644 --- a/devicesetup/src/main/java/io/particle/android/sdk/devicesetup/apconnector/ApConnectorApi29.kt +++ b/devicesetup/src/main/java/io/particle/android/sdk/devicesetup/apconnector/ApConnectorApi29.kt @@ -74,6 +74,7 @@ class ApConnectorApi29( ): ConnectivityManager.NetworkCallback { return object : ConnectivityManager.NetworkCallback() { + @RequiresApi(VERSION_CODES.M) override fun onAvailable(network: Network) { log.info { "onAvailable: $network" } decoratingClient.onApConnectionSuccessful(config) @@ -108,6 +109,7 @@ class ApConnectorApi29( } } + @RequiresApi(VERSION_CODES.M) private fun clearState() { try { DeviceSetupState.networkCallbacks = networkCallbacks diff --git a/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java b/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java index a4688865..d83321bf 100644 --- a/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java +++ b/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java @@ -121,9 +121,10 @@ public Network getNetworkObjectForCurrentWifiConnection() { // Instead, you have to infer it based on the fact that you can only // have one connected Wi-Fi connection at a time. // (Update: one *regular* Wi-Fi connection, anyway. See below.) + Network[] networks = connectivityManager.getAllNetworks(); - return Funcy.findFirstMatch( - Arrays.asList(connectivityManager.getAllNetworks()), + Network selectedNetwork = Funcy.findFirstMatch( + Arrays.asList(networks), network -> { NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); if (capabilities == null) { @@ -133,9 +134,15 @@ public Network getNetworkObjectForCurrentWifiConnection() { if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) { return false; } + // Don't use any connections if they have internet! + if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + return false; + } return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); } ); + + return selectedNetwork; } public int addNetwork(WifiConfiguration config) { diff --git a/ecjpake4j/build.gradle b/ecjpake4j/build.gradle index 14cf9a6a..8dc5a335 100644 --- a/ecjpake4j/build.gradle +++ b/ecjpake4j/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'kotlin-android' android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/firmwareprotos/build.gradle b/firmwareprotos/build.gradle index 9911f2d7..b33c9f36 100644 --- a/firmwareprotos/build.gradle +++ b/firmwareprotos/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" } diff --git a/mesh/build.gradle b/mesh/build.gradle index 5b203245..2f4e61be 100644 --- a/mesh/build.gradle +++ b/mesh/build.gradle @@ -5,11 +5,11 @@ apply plugin: "androidx.navigation.safeargs.kotlin" android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/meshui/build.gradle b/meshui/build.gradle index a8ef63dc..cbe5c7b2 100644 --- a/meshui/build.gradle +++ b/meshui/build.gradle @@ -5,11 +5,11 @@ apply plugin: "androidx.navigation.safeargs.kotlin" android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/sdk_example_app/build.gradle b/sdk_example_app/build.gradle index ed7b5c80..ddabfe1e 100644 --- a/sdk_example_app/build.gradle +++ b/sdk_example_app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { applicationId "io.particle.cloudsdk.example_app" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" } diff --git a/sdk_example_app/src/main/AndroidManifest.xml b/sdk_example_app/src/main/AndroidManifest.xml index faceecda..d7567e71 100644 --- a/sdk_example_app/src/main/AndroidManifest.xml +++ b/sdk_example_app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:theme="@style/AppTheme"> @@ -18,16 +19,19 @@ - + \ No newline at end of file diff --git a/setup_exampleapp/build.gradle b/setup_exampleapp/build.gradle index 79d2b253..f951a60c 100644 --- a/setup_exampleapp/build.gradle +++ b/setup_exampleapp/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { applicationId "io.particle.devicesetup.exampleapp" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/setup_exampleapp/src/main/AndroidManifest.xml b/setup_exampleapp/src/main/AndroidManifest.xml index 26865c5c..275ea45b 100644 --- a/setup_exampleapp/src/main/AndroidManifest.xml +++ b/setup_exampleapp/src/main/AndroidManifest.xml @@ -6,9 +6,11 @@ android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:exported="true" android:theme="@style/AppTheme" > diff --git a/setup_testapp/build.gradle b/setup_testapp/build.gradle index b73abf59..0ebe9d69 100644 --- a/setup_testapp/build.gradle +++ b/setup_testapp/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 + compileSdkVersion 33 defaultConfig { applicationId "io.particle.devicesetup.testapp" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/setup_testapp/src/main/AndroidManifest.xml b/setup_testapp/src/main/AndroidManifest.xml index 2d7cdba5..6aab9093 100644 --- a/setup_testapp/src/main/AndroidManifest.xml +++ b/setup_testapp/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:exported="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> diff --git a/setup_testapp/src/main/res/layout/activity_main.xml b/setup_testapp/src/main/res/layout/activity_main.xml index a983a125..0b79c082 100644 --- a/setup_testapp/src/main/res/layout/activity_main.xml +++ b/setup_testapp/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - + From 61c3bf975bfc1fbeff4f47b28e4c0b8fd3b71809 Mon Sep 17 00:00:00 2001 From: Nick Lambourne Date: Thu, 13 Jul 2023 14:48:48 +0100 Subject: [PATCH 2/2] Update comment --- .../android/sdk/utils/WifiFacade.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java b/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java index d83321bf..87397f9e 100644 --- a/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java +++ b/devicesetup/src/main/java/io/particle/android/sdk/utils/WifiFacade.java @@ -135,6 +135,27 @@ public Network getNetworkObjectForCurrentWifiConnection() { return false; } // Don't use any connections if they have internet! + // Note: Due to a known issue with certain Android phones (e.g., Pixel 6/7 series) running Android 12 or above, + // these devices may retain access to their current Wi-Fi network while also connecting to a Particle Wi-Fi Module + // in SoftAP mode. This behavior is due to a feature introduced in Android 12 known as Wi-Fi STA/STA concurrency, + // which allows devices to connect to two Wi-Fi networks concurrently. + // + // The correct way to handle this programmatically using Android's ConnectivityManager APIs (SDK 31+) has proven + // to be unreliable due to the bugs tracked at: + // https://issuetracker.google.com/issues/249023377 + // https://issuetracker.google.com/issues/232107693 + // + // Exhaustive testing was done after implementing this the correct way and it just didn't work. + // Before giving up, the Android source code was examined to see how the system itself handles this situation. + // In that code, we found a similar hack to the one below, which is used to determine whether a network is + // a "valid" internet connection. If it is, then the system will not bind to it. + // + // The current workaround involves excluding all networks with internet capability when binding to a network. + // While this is not a perfect fix (as future changes in Android might affect its effectiveness), this solution + // has proven to be reliable for the time being and manages to address the issue across all Pixel phones tested, + // ranging from the latest models back to those running Android 5. + // + // For more information, refer to the aforementioned issue trackers. if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { return false; }