-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
kaspresso/src/main/kotlin/com/kaspersky/kaspresso/device/bluetooth/Bluetooth.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.kaspersky.kaspresso.device.bluetooth | ||
|
||
/** | ||
* The interface to work with bluetooth settings. | ||
* | ||
* Required: Started AdbServer | ||
* 1. Download a file "kaspresso/artifacts/adbserver-desktop.jar" | ||
* 2. Start AdbServer => input in cmd "java jar path_to_file/adbserver-desktop.jar" | ||
* Methods demanding to use AdbServer in the default implementation of this interface are marked. | ||
* But nobody can't deprecate you to write implementation that doesn't require AdbServer. | ||
*/ | ||
interface Bluetooth { | ||
|
||
/** | ||
* Enables Bluetooth on the device using adb. | ||
*/ | ||
fun enable() | ||
|
||
/** | ||
* Disables Bluetooth on the device using adb. | ||
*/ | ||
fun disable() | ||
} |
120 changes: 120 additions & 0 deletions
120
kaspresso/src/main/kotlin/com/kaspersky/kaspresso/device/bluetooth/BluetoothImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package com.kaspersky.kaspresso.device.bluetooth | ||
|
||
import android.bluetooth.BluetoothManager | ||
import android.content.Context | ||
import com.kaspersky.components.kautomator.system.UiSystem | ||
import com.kaspersky.kaspresso.device.server.AdbServer | ||
import com.kaspersky.kaspresso.flakysafety.algorithm.FlakySafetyAlgorithm | ||
import com.kaspersky.kaspresso.internal.exceptions.AdbServerException | ||
import com.kaspersky.kaspresso.internal.systemscreen.NotificationsFullScreen | ||
|
||
import com.kaspersky.kaspresso.logger.UiTestLogger | ||
import com.kaspersky.kaspresso.params.FlakySafetyParams | ||
|
||
/** | ||
* The implementation of the [Bluetooth] interface. | ||
*/ | ||
class BluetoothImpl( | ||
private val logger: UiTestLogger, | ||
private val targetContext: Context, | ||
private val adbServer: AdbServer | ||
) : Bluetooth { | ||
|
||
companion object { | ||
private const val CMD_STATE_ENABLE = "enable" | ||
private const val CMD_STATE_DISABLE = "disable" | ||
private const val BLUETOOTH_STATE_CHANGE_CMD = "svc bluetooth" | ||
private const val BLUETOOTH_STATE_CHANGE_ROOT_CMD = "su 0 svc bluetooth" | ||
private const val BLUETOOTH_STATE_CHECK_CMD = "settings get global bluetooth_on" | ||
private const val BLUETOOTH_STATE_CHECK_RESULT_ENABLED = "1" | ||
private const val BLUETOOTH_STATE_CHECK_RESULT_DISABLED = "0" | ||
private val ADB_RESULT_REGEX = Regex("exitCode=(\\d+), message=(.+)") | ||
} | ||
|
||
private val flakySafetyAlgorithm = FlakySafetyAlgorithm(logger) | ||
private val flakySafetyParams: FlakySafetyParams | ||
get() = FlakySafetyParams( | ||
timeoutMs = 1000, | ||
intervalMs = 100, | ||
allowedExceptions = setOf(AdbServerException::class.java) | ||
) | ||
|
||
override fun enable() { | ||
toggleBluetooth(enable = true) | ||
} | ||
|
||
override fun disable() { | ||
toggleBluetooth(enable = false) | ||
} | ||
|
||
/** | ||
* Toggles Bluetooth state | ||
* Tries, first and foremost, to send ADB command. If this attempt fails, | ||
* opens Android Settings screen and tries to switch Bluetooth setting thumb. | ||
*/ | ||
private fun toggleBluetooth(enable: Boolean) { | ||
if (isBluetoothNotSupported()) { | ||
logger.i("Bluetooth is not supported") | ||
return | ||
} | ||
logger.i("${if (enable) "En" else "Dis"}able bluetooth") | ||
if (!changeBluetoothStateUsingAdbServer(enable, BLUETOOTH_STATE_CHANGE_ROOT_CMD) && | ||
!changeBluetoothStateUsingAdbServer(enable, BLUETOOTH_STATE_CHANGE_CMD) | ||
) { | ||
toggleBluetoothUsingAndroidSettings(enable) | ||
logger.i("Bluetooth ${if (enable) "en" else "dis"}abled") | ||
} | ||
} | ||
|
||
/** | ||
* Tries to change Bluetooth state using AdbServer if it is available | ||
* @return true if Bluetooth state changed or false otherwise | ||
*/ | ||
private fun changeBluetoothStateUsingAdbServer(isEnabled: Boolean, changeCommand: String): Boolean = | ||
try { | ||
val (state, expectedResult) = when (isEnabled) { | ||
true -> CMD_STATE_ENABLE to BLUETOOTH_STATE_CHECK_RESULT_ENABLED | ||
false -> CMD_STATE_DISABLE to BLUETOOTH_STATE_CHECK_RESULT_DISABLED | ||
} | ||
adbServer.performShell("$changeCommand $state") | ||
flakySafetyAlgorithm.invokeFlakySafely(flakySafetyParams) { | ||
val result = adbServer.performShell(BLUETOOTH_STATE_CHECK_CMD) | ||
if (parseAdbResponse(result)?.trim() == expectedResult) true else | ||
throw AdbServerException("Failed to change Bluetooth state using ABD") | ||
} | ||
} catch (e: AdbServerException) { | ||
false | ||
} | ||
|
||
@Suppress("MagicNumber") | ||
private fun toggleBluetoothUsingAndroidSettings(enable: Boolean) { | ||
val height = targetContext.resources.displayMetrics.heightPixels | ||
val width = targetContext.resources.displayMetrics.widthPixels | ||
|
||
UiSystem { | ||
drag(width / 2, 0, width / 2, (height * 0.67).toInt(), 50) | ||
} | ||
UiSystem { | ||
drag(width / 2, 0, width / 2, (height * 0.67).toInt(), 50) | ||
} | ||
NotificationsFullScreen { | ||
bluetoothSwitch.setChecked(enable) | ||
} | ||
UiSystem { | ||
drag(width / 2, height, width / 2, 0, 50) | ||
} | ||
UiSystem { | ||
drag(width / 2, height, width / 2, 0, 50) | ||
} | ||
} | ||
|
||
private fun isBluetoothNotSupported(): Boolean = | ||
(this.targetContext.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter == null | ||
|
||
private fun parseAdbResponse(response: List<String>): String? { | ||
val result = response.firstOrNull()?.lineSequence()?.first() ?: return null | ||
val match = ADB_RESULT_REGEX.find(result) ?: return null | ||
val (_, message) = match.destructured | ||
return message | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
.../androidTest/kotlin/com/kaspersky/kaspressample/device_tests/DeviceBluetoothSampleTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.kaspersky.kaspressample.device_tests | ||
|
||
import android.bluetooth.BluetoothAdapter | ||
import android.bluetooth.BluetoothManager | ||
import android.content.Context | ||
import androidx.test.ext.junit.rules.activityScenarioRule | ||
import com.kaspersky.kaspressample.device.DeviceSampleActivity | ||
import com.kaspersky.kaspressample.utils.SafeAssert.assertFalseSafely | ||
import com.kaspersky.kaspressample.utils.SafeAssert.assertTrueSafely | ||
import com.kaspersky.kaspresso.device.Device | ||
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase | ||
import com.kaspersky.kaspresso.testcases.core.testcontext.BaseTestContext | ||
import org.junit.Rule | ||
import org.junit.Test | ||
|
||
class DeviceBluetoothSampleTest : TestCase() { | ||
|
||
@get:Rule | ||
val activityRule = activityScenarioRule<DeviceSampleActivity>() | ||
|
||
@Test | ||
fun bluetoothSampleTest() { | ||
before { | ||
tryToggleBluetooth(shouldEnable = true) | ||
}.after { | ||
tryToggleBluetooth(shouldEnable = true) | ||
}.run { | ||
|
||
step("Disable bluetooth") { | ||
tryToggleBluetooth(shouldEnable = false) | ||
checkBluetooth(shouldBeEnabled = false) | ||
} | ||
|
||
step("Enable bluetooth") { | ||
tryToggleBluetooth(shouldEnable = true) | ||
checkBluetooth(shouldBeEnabled = true) | ||
} | ||
} | ||
} | ||
|
||
private fun tryToggleBluetooth(shouldEnable: Boolean) { | ||
if (shouldEnable) { | ||
device.bluetooth.enable() | ||
} else { | ||
device.bluetooth.disable() | ||
} | ||
} | ||
|
||
private fun BaseTestContext.checkBluetooth(shouldBeEnabled: Boolean) { | ||
try { | ||
if (shouldBeEnabled) assertTrueSafely { isBluetoothEnabled() } else assertFalseSafely { isBluetoothEnabled() } | ||
} catch (assertionError: AssertionError) { | ||
if (isBluetoothNotSupported()) return | ||
else throw assertionError | ||
} | ||
} | ||
|
||
private fun isBluetoothNotSupported(): Boolean = | ||
device.getBluetoothAdapter() == null | ||
|
||
private fun isBluetoothEnabled(): Boolean = | ||
device.getBluetoothAdapter()?.isEnabled ?: false | ||
|
||
private fun Device.getBluetoothAdapter(): BluetoothAdapter? = | ||
(this.targetContext.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters