Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c79db7ab authored by Josh Wu's avatar Josh Wu
Browse files

Log previous state info in BluetoothEnabledStateChanged

Flag: EXEMPT only a new log field added
Test: atest BluetoothMetricsTests
Bug: 278672462
Change-Id: I0f39f4ef8a84df026893097ace77e9010decb302
parent 654ff67d
Loading
Loading
Loading
Loading
+34 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.bluetooth

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.MacAddress
@@ -30,10 +32,12 @@ import io.grpc.okhttp.OkHttpChannelBuilder
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -46,6 +50,7 @@ import pandora.HostGrpc
import pandora.HostProto.ConnectRequest
import pandora.HostProto.DisconnectRequest

@Suppress("DEPRECATION")
@kotlinx.coroutines.ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class BluetoothMetricsHelperTest {
@@ -68,7 +73,23 @@ class BluetoothMetricsHelperTest {
    private val testDispatcher = UnconfinedTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private val context = InstrumentationRegistry.getInstrumentation().getContext()
    private val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java)!!.adapter

    private val bluetoothAdapter: BluetoothAdapter =
        context.getSystemService(BluetoothManager::class.java)!!.adapter
    private val adapterState = MutableStateFlow(bluetoothAdapter.state)

    init {
        context.registerReceiver(
            object : BroadcastReceiver() {
                override fun onReceive(context: Context, intent: Intent) {
                    adapterState.tryEmit(
                        intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
                    )
                }
            },
            IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED)
        )
    }

    @Before
    fun setUp() {
@@ -92,6 +113,10 @@ class BluetoothMetricsHelperTest {
        mHostBlockingStub = HostGrpc.newBlockingStub(mChannel)
        mHostStub = HostGrpc.newStub(mChannel)
        mHostBlockingStub.withWaitForReady()?.readLocalAddress(Empty.getDefaultInstance())

        // Make sure the Adapter is on.
        bluetoothAdapter.enable()
        runBlocking { adapterState.first { it == BluetoothAdapter.STATE_ON } }
    }

    @After
@@ -132,4 +157,12 @@ class BluetoothMetricsHelperTest {
            DisconnectRequest.newBuilder().setConnection(connectResponse.connection).build()
        mHostBlockingStub.disconnect(disconnectRequest)
    }

    @Test
    fun testBluetoothDisableEnable() = runTest {
        bluetoothAdapter.disable()
        adapterState.first { it == BluetoothAdapter.STATE_OFF }
        bluetoothAdapter.enable()
        adapterState.first { it == BluetoothAdapter.STATE_ON }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@ java_test_host {
        ":BluetoothMetricsTestApp",
    ],

    data_native_bins: [
        "bumble_pandora_server",
    ],

    required: ["bumble_pandora_server"],

    test_suites: [
+3 −1
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@
    <target_preparer class="com.android.tradefed.targetprep.RunHostCommandTargetPreparer">
      <option name="python-virtualenv" value="true"/>
      <option name="host-setup-command" value="adb -s $SERIAL reverse tcp:7999 tcp:7999" />
      <option name="host-setup-command" value="adb -s $SERIAL forward tcp:6211 vsock:2:7300" />
      <option name="host-teardown-command" value="adb -s $SERIAL forward --remove tcp:6211" />
      <option name="host-background-command"
        value="$EXTRA_FILE(host_testcases)/bumble_pandora_server/x86_64/bumble_pandora_server"/>
        value="$EXTRA_FILE(host_testcases)/BluetoothMetricsTests/bumble_pandora_server --rootcanal-port 6211"/>
      <option name="host-teardown-command" value="adb -s $SERIAL reverse --remove tcp:7999" />
    </target_preparer>
    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+32 −17
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import android.cts.statsdatom.lib.ConfigUtils
import android.cts.statsdatom.lib.DeviceUtils
import android.cts.statsdatom.lib.ReportUtils
import com.android.os.AtomsProto
import com.android.os.AtomsProto.BluetoothEnabledStateChanged
import com.android.os.StatsLog
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
import com.google.common.truth.Truth.assertThat
import java.time.Duration
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,25 +46,38 @@ class MetricsTest : BaseHostJUnit4Test() {
    }

    @Test
    fun aclMetricTest() {
        val data = uploadAtomConfigAndTriggerTest("incomingClassicConnectionTest")
        assertThat(data.size).isAtLeast(2)
        val atom1 = data.get(0).getAtom().getBluetoothAclConnectionStateChanged()
        assertThat(atom1.getState()).isEqualTo(ConnectionStateEnum.CONNECTION_STATE_CONNECTED)
        assertThat(atom1.getTransport()).isEqualTo(TransportTypeEnum.TRANSPORT_TYPE_BREDR)
        val atom2 = data.get(1).getAtom().getBluetoothAclConnectionStateChanged()
        assertThat(atom2.getState()).isEqualTo(ConnectionStateEnum.CONNECTION_STATE_DISCONNECTED)
        assertThat(atom2.getTransport()).isEqualTo(TransportTypeEnum.TRANSPORT_TYPE_BREDR)
        assertThat(atom2.getMetricId()).isEqualTo(atom1.getMetricId())
    fun testBluetoothDisableEnable_shouldProduceEnabledStateChanged() {
        val data =
            uploadAtomConfigAndTriggerTest(
                "testBluetoothDisableEnable",
                intArrayOf(AtomsProto.Atom.BLUETOOTH_ENABLED_STATE_CHANGED_FIELD_NUMBER)
            )
        // First atom might be the setup one.
        val offset =
            data[0].atom.bluetoothEnabledStateChanged.let {
                if (it.state == BluetoothEnabledStateChanged.State.ENABLED) {
                    1
                } else {
                    0
                }
            }
        data[offset].atom.bluetoothEnabledStateChanged.apply {
            assertThat(state).isEqualTo(BluetoothEnabledStateChanged.State.DISABLED)
            assertThat(previousState).isEqualTo(BluetoothEnabledStateChanged.State.ENABLED)
            assertThat(timeSinceLastChangedMillis).isGreaterThan(Duration.ofMillis(1).toMillis())
        }
        data[offset + 1].atom.bluetoothEnabledStateChanged.apply {
            assertThat(state).isEqualTo(BluetoothEnabledStateChanged.State.ENABLED)
            assertThat(previousState).isEqualTo(BluetoothEnabledStateChanged.State.DISABLED)
            assertThat(timeSinceLastChangedMillis).isGreaterThan(Duration.ofMillis(1).toMillis())
        }
    }

    private fun uploadAtomConfigAndTriggerTest(testName: String): List<StatsLog.EventMetricData> {
        val device = getDevice()
        ConfigUtils.uploadConfigForPushedAtoms(
            device,
            TEST_APP_PKG_NAME,
            intArrayOf(AtomsProto.Atom.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED_FIELD_NUMBER)
        )
    private fun uploadAtomConfigAndTriggerTest(
        testName: String,
        atoms: IntArray
    ): List<StatsLog.EventMetricData> {
        ConfigUtils.uploadConfigForPushedAtoms(device, TEST_APP_PKG_NAME, atoms)

        DeviceUtils.runDeviceTests(device, TEST_APP_PKG_NAME, TEST_APP_CLASS_NAME, testName)

+26 −1
Original line number Diff line number Diff line
@@ -256,6 +256,14 @@ class BluetoothManagerService {
                    + mPackageName;
        }

        long getTimestamp() {
            return mTimestamp;
        }

        boolean getEnable() {
            return mEnable;
        }

        void dump(ProtoOutputStream proto) {
            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp);
            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable);
@@ -2171,6 +2179,7 @@ class BluetoothManagerService {
    }

    private void addActiveLog(int reason, String packageName, boolean enable) {
        ActiveLog lastActiveLog = mActiveLogs.peekLast();
        synchronized (mActiveLogs) {
            if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) {
                mActiveLogs.remove();
@@ -2182,13 +2191,29 @@ class BluetoothManagerService {
                            ? BluetoothStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED
                            : BluetoothStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;

            int lastState;
            long timeSinceLastChanged;
            if (lastActiveLog == null) {
                lastState = BluetoothStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__UNKNOWN;
                timeSinceLastChanged = 0;
            } else {
                lastState =
                        lastActiveLog.getEnable()
                                ? BluetoothStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED
                                : BluetoothStatsLog
                                        .BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
                timeSinceLastChanged = System.currentTimeMillis() - lastActiveLog.getTimestamp();
            }

            BluetoothStatsLog.write_non_chained(
                    BluetoothStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
                    Binder.getCallingUid(),
                    null,
                    state,
                    reason,
                    packageName);
                    packageName,
                    lastState,
                    timeSinceLastChanged);
        }
    }