Disable IWLAN in tests on 24Q2

24Q2 reconnects mobile data when Wi-Fi is toggled when IWLAN is used,
which breaks multinetwork tests. Disable IWLAN as a workaround on such
builds.

Bug: 368477391
Test: atest
Change-Id: Ia5748dc2378c5fc4a43ff83e6b47e629eb8bf77a
This commit is contained in:
Remi NGUYEN VAN 2024-10-30 13:11:20 +09:00
commit 1372ac2140
2 changed files with 143 additions and 6 deletions

View file

@ -0,0 +1,132 @@
/*
* 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.testutils.connectivitypreparer
import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.READ_PHONE_STATE
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS
import android.content.pm.PackageManager.FEATURE_WIFI
import android.os.Build
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import android.os.ParcelFileDescriptor
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
import android.telephony.SubscriptionManager
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.net.module.util.ArrayTrackRecord
import com.android.testutils.runAsShell
import kotlin.test.assertNotNull
import org.junit.Test
import org.junit.runner.RunWith
private const val CONFIG_CHANGE_TIMEOUT_MS = 10_000L
private val TAG = CarrierConfigSetupTest::class.simpleName
@RunWith(AndroidJUnit4::class)
class CarrierConfigSetupTest {
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
private val pm by lazy { context.packageManager }
private val carrierConfigManager by lazy {
context.getSystemService(CarrierConfigManager::class.java)
}
@Test
fun testSetCarrierConfig() {
if (!shouldDisableIwlan()) return
overrideAllSubscriptions(PersistableBundle().apply {
putBoolean(CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false)
})
}
@Test
fun testClearCarrierConfig() {
// set/clear are in different test runs so it is difficult to share state between them.
// The conditions to disable IWLAN should not change over time (in particular
// force_iwlan_mms is a readonly flag), so just perform the same check again on teardown.
// CarrierConfigManager overrides are cleared on reboot by default anyway, so any missed
// cleanup should not be too damaging.
if (!shouldDisableIwlan()) return
overrideAllSubscriptions(null)
}
private class ConfigChangedReceiver : BroadcastReceiver() {
val receivedSubIds = ArrayTrackRecord<Int>()
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != ACTION_CARRIER_CONFIG_CHANGED) return
val subIdx = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, -1)
// It is possible this is a configuration change for a different setting, so the test
// may not wait for long enough. Unfortunately calling CarrierConfigManager to check
// if the config was applied does not help because it will always return the override,
// even if it was not applied to the subscription yet.
// In practice, it is very unlikely that a different broadcast arrives, and then a test
// flakes because of the iwlan behavior in the time it takes for the config to be
// applied.
Log.d(TAG, "Received config change for sub $subIdx")
receivedSubIds.add(subIdx)
}
}
private fun overrideAllSubscriptions(bundle: PersistableBundle?) {
runAsShell(READ_PHONE_STATE, MODIFY_PHONE_STATE) {
val receiver = ConfigChangedReceiver()
context.registerReceiver(receiver, IntentFilter(ACTION_CARRIER_CONFIG_CHANGED))
val subscriptions = context.getSystemService(SubscriptionManager::class.java)
.activeSubscriptionInfoList
subscriptions?.forEach { subInfo ->
Log.d(TAG, "Overriding config for subscription $subInfo")
carrierConfigManager.overrideConfig(subInfo.subscriptionId, bundle)
}
// Don't wait after each update before the next one, but expect all updates to be done
// eventually
subscriptions?.forEach { subInfo ->
assertNotNull(receiver.receivedSubIds.poll(CONFIG_CHANGE_TIMEOUT_MS, pos = 0) {
it == subInfo.subscriptionId
}, "Config override broadcast not received for subscription $subInfo")
}
}
}
private fun shouldDisableIwlan(): Boolean {
// IWLAN on U 24Q2 release (U QPR3) causes cell data to reconnect when Wi-Fi is toggled due
// to the implementation of the force_iwlan_mms feature, which does not work well with
// multinetworking tests. Disable the feature on such builds (b/368477391).
// The behavior changed in more recent releases (V) so only U 24Q2 is affected.
return pm.hasSystemFeature(FEATURE_TELEPHONY_IMS) && pm.hasSystemFeature(FEATURE_WIFI) &&
Build.VERSION.SDK_INT == UPSIDE_DOWN_CAKE &&
isForceIwlanMmsEnabled()
}
private fun isForceIwlanMmsEnabled(): Boolean {
val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
val flagEnabledRegex = Regex(
"""telephony/com\.android\.internal\.telephony\.flags\.force_iwlan_mms:""" +
""".*ENABLED \(system\)""")
ParcelFileDescriptor.AutoCloseInputStream(
uiAutomation.executeShellCommand("printflags")).bufferedReader().use { reader ->
return reader.lines().anyMatch {
it.contains(flagEnabledRegex)
}
}
}
}

View file

@ -28,6 +28,7 @@ import com.android.tradefed.targetprep.suite.SuiteApkInstaller
private const val CONNECTIVITY_CHECKER_APK = "ConnectivityTestPreparer.apk"
private const val CONNECTIVITY_PKG_NAME = "com.android.testutils.connectivitypreparer"
private const val CONNECTIVITY_CHECK_CLASS = "$CONNECTIVITY_PKG_NAME.ConnectivityCheckTest"
private const val CARRIER_CONFIG_SETUP_CLASS = "$CONNECTIVITY_PKG_NAME.CarrierConfigSetupTest"
// As per the <instrumentation> defined in the checker manifest
private const val CONNECTIVITY_CHECK_RUNNER_NAME = "androidx.test.runner.AndroidJUnitRunner"
@ -84,27 +85,28 @@ open class ConnectivityTestTargetPreparer : BaseTargetPreparer() {
installer.setShouldGrantPermission(true)
installer.setUp(testInfo)
val testMethods = mutableListOf<String>()
val testMethods = mutableListOf<Pair<String, String>>()
if (!ignoreWifiCheck) {
testMethods.add("testCheckWifiSetup")
testMethods.add(CONNECTIVITY_CHECK_CLASS to "testCheckWifiSetup")
}
if (!ignoreMobileDataCheck) {
testMethods.add("testCheckTelephonySetup")
testMethods.add(CARRIER_CONFIG_SETUP_CLASS to "testSetCarrierConfig")
testMethods.add(CONNECTIVITY_CHECK_CLASS to "testCheckTelephonySetup")
}
testMethods.forEach {
runTestMethod(testInfo, it)
runTestMethod(testInfo, it.first, it.second)
}
}
private fun runTestMethod(testInfo: TestInformation, method: String) {
private fun runTestMethod(testInfo: TestInformation, clazz: String, method: String) {
val runner = DefaultRemoteAndroidTestRunner(
CONNECTIVITY_PKG_NAME,
CONNECTIVITY_CHECK_RUNNER_NAME,
testInfo.device.iDevice
)
runner.runOptions = "--no-hidden-api-checks"
runner.setMethodName(CONNECTIVITY_CHECK_CLASS, method)
runner.setMethodName(clazz, method)
val receiver = CollectingTestListener()
if (!testInfo.device.runInstrumentationTests(runner, receiver)) {
@ -187,6 +189,9 @@ open class ConnectivityTestTargetPreparer : BaseTargetPreparer() {
override fun tearDown(testInfo: TestInformation, e: Throwable?) {
if (isTearDownDisabled) return
if (!ignoreMobileDataCheck) {
runTestMethod(testInfo, CARRIER_CONFIG_SETUP_CLASS, "testClearCarrierConfig")
}
installer.tearDown(testInfo, e)
setUpdaterNetworkingEnabled(
testInfo,