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

Commit d984c29b authored by Aritra Sen's avatar Aritra Sen
Browse files

Move Profile connection state changes logic inside the handler to make sure...

Move Profile connection state changes logic inside the handler to make sure concurrent counter updates are handled correctly.

Add flagging for queueing logic for Profile connection change in scan manager.

Bug: 302620968
Bug: 306033108
Test: atest com.android.bluetooth.gatt.ScanManagerTest#multipleProfileConnectionStateChanged_updateCountersCorrectly --rerun-until-failure=100
Change-Id: I72cb908bba21d87a4f429a679fdd26afc569d4c8
parent e757a02d
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.BluetoothAdapterProxy;
import com.android.bluetooth.flags.FeatureFlagsImpl;

/**
 * Factory class for object initialization to help with unit testing
@@ -70,7 +71,8 @@ public class GattObjectsFactory {

    public ScanManager createScanManager(GattService service, AdapterService adapterService,
            BluetoothAdapterProxy bluetoothAdapterProxy) {
        return new ScanManager(service, adapterService, bluetoothAdapterProxy);
        return new ScanManager(
                service, adapterService, bluetoothAdapterProxy, new FeatureFlagsImpl());
    }

    public PeriodicScanManager createPeriodicScanManager(AdapterService adapterService) {
+52 −3
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.view.Display;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.BluetoothAdapterProxy;
import com.android.bluetooth.flags.FeatureFlags;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

@@ -56,6 +57,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -102,6 +104,7 @@ public class ScanManager {
    static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9;
    static final int MSG_START_CONNECTING = 10;
    static final int MSG_STOP_CONNECTING = 11;
    private static final int MSG_BT_PROFILE_CONN_STATE_CHANGED = 12;
    private static final String ACTION_REFRESH_BATCHED_SCAN =
            "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN";

@@ -118,6 +121,7 @@ public class ScanManager {
    private int mCurUsedTrackableAdvertisements = 0;
    private final GattService mService;
    private final AdapterService mAdapterService;
    private final FeatureFlags mFeatureFlags;
    private BroadcastReceiver mBatchAlarmReceiver;
    private boolean mBatchAlarmReceiverRegistered;
    private ScanNative mScanNative;
@@ -142,7 +146,8 @@ public class ScanManager {
    private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray();
    private boolean mScreenOn = false;
    @VisibleForTesting boolean mIsConnecting;
    private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
    @VisibleForTesting int mProfilesConnecting;
    private int mProfilesConnected, mProfilesDisconnecting;

    @VisibleForTesting
    static class UidImportance {
@@ -155,8 +160,11 @@ public class ScanManager {
        }
    }

    ScanManager(GattService service, AdapterService adapterService,
            BluetoothAdapterProxy bluetoothAdapterProxy) {
    ScanManager(
            GattService service,
            AdapterService adapterService,
            BluetoothAdapterProxy bluetoothAdapterProxy,
            FeatureFlags featureFlags) {
        mRegularScanClients =
                Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
        mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
@@ -170,6 +178,7 @@ public class ScanManager {
        mLocationManager = mAdapterService.getSystemService(LocationManager.class);
        mBluetoothAdapterProxy = bluetoothAdapterProxy;
        mIsConnecting = false;
        mFeatureFlags = Objects.requireNonNull(featureFlags, "Feature Flags cannot be null");

        mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0);
        mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1);
@@ -374,6 +383,8 @@ public class ScanManager {
                case MSG_STOP_CONNECTING:
                    handleClearConnectingState();
                    break;
                case MSG_BT_PROFILE_CONN_STATE_CHANGED:
                    handleProfileConnectionStateChanged(msg);
                default:
                    // Shouldn't happen.
                    Log.e(TAG, "received an unkown message : " + msg.what);
@@ -899,6 +910,31 @@ public class ScanManager {
                mScanNative.configureRegularScanParams();
            }
        }

        private void handleProfileConnectionStateChanged(Message msg) {
            int fromState = msg.arg1, toState = msg.arg2;
            int profile = ((Integer) msg.obj).intValue();
            boolean updatedConnectingState =
                    updateCountersAndCheckForConnectingState(toState, fromState);
            if (DBG) {
                Log.d(
                        TAG,
                        "PROFILE_CONNECTION_STATE_CHANGE:"
                                + (" profile=" + BluetoothProfile.getProfileName(profile))
                                + (" prevState=" + fromState)
                                + (" state=" + toState)
                                + (" updatedConnectingState = " + updatedConnectingState));
            }
            if (updatedConnectingState) {
                if (!mIsConnecting) {
                    handleConnectingState();
                }
            } else {
                if (mIsConnecting) {
                    handleClearConnectingState();
                }
            }
        }
    }

    /**
@@ -2011,6 +2047,19 @@ public class ScanManager {
     */
    public void handleBluetoothProfileConnectionStateChanged(
            int profile, int fromState, int toState) {
        if (mFeatureFlags.enableQueuingProfileConnectionChange()) {
            if (mHandler == null) {
                Log.d(TAG, "handleBluetoothProfileConnectionStateChanged: mHandler is null.");
                return;
            }
            mHandler.obtainMessage(
                            MSG_BT_PROFILE_CONN_STATE_CHANGED,
                            fromState,
                            toState,
                            Integer.valueOf(profile))
                    .sendToTarget();
            return;
        }
        boolean updatedConnectingState =
                updateCountersAndCheckForConnectingState(toState, fromState);
        if (DBG) {
+49 −1
Original line number Diff line number Diff line
@@ -68,11 +68,14 @@ import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.BluetoothAdapterProxy;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.flags.FakeFeatureFlagsImpl;
import com.android.bluetooth.flags.Flags;
import com.android.internal.app.IBatteryStats;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.junit.After;
@@ -124,6 +127,7 @@ public class ScanManagerTest {
    @Mock private ScanNativeInterface mScanNativeInterface;
    @Mock private MetricsLogger  mMetricsLogger;

    private FakeFeatureFlagsImpl mFakeFlagsImpl;
    private MockContentResolver mMockContentResolver;
    @Captor ArgumentCaptor<Long> mScanDurationCaptor;

@@ -181,7 +185,10 @@ public class ScanManagerTest {
        doReturn(mTargetContext.getUser()).when(mMockGattService).getUser();
        doReturn(mTargetContext.getPackageName()).when(mMockGattService).getPackageName();

        mScanManager = new ScanManager(mMockGattService, mAdapterService, mBluetoothAdapterProxy);
        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
        mScanManager =
                new ScanManager(
                        mMockGattService, mAdapterService, mBluetoothAdapterProxy, mFakeFlagsImpl);

        mHandler = mScanManager.getClientHandler();
        assertThat(mHandler).isNotNull();
@@ -1360,6 +1367,10 @@ public class ScanManagerTest {

    @Test
    public void profileConnectionStateChanged_sendStartConnectionMessage() {
        // This test shouldn't be impacted by this flag. Setting it to false to ensure it passes
        // with the older behavior. Tested flag set to true locally to make sure it doesn't fail
        // when flag is removed.
        mFakeFlagsImpl.setFlag(Flags.FLAG_ENABLE_QUEUING_PROFILE_CONNECTION_CHANGE, false);
        // Set scan downgrade duration through Mock
        when(mAdapterService.getScanDowngradeDurationMillis())
                .thenReturn((long) DELAY_SCAN_DOWNGRADE_DURATION_MS);
@@ -1374,4 +1385,41 @@ public class ScanManagerTest {
        TestUtils.waitForLooperToBeIdle(mHandler.getLooper());
        assertThat(mScanManager.mIsConnecting).isTrue();
    }

    @Test
    public void multipleProfileConnectionStateChanged_updateCountersCorrectly()
            throws ExecutionException, InterruptedException {
        mFakeFlagsImpl.setFlag(Flags.FLAG_ENABLE_QUEUING_PROFILE_CONNECTION_CHANGE, true);
        when(mAdapterService.getScanDowngradeDurationMillis())
                .thenReturn((long) DELAY_SCAN_DOWNGRADE_DURATION_MS);
        assertThat(mScanManager.mIsConnecting).isFalse();

        Thread t1 =
                new Thread(
                        () ->
                                mScanManager.handleBluetoothProfileConnectionStateChanged(
                                        BluetoothProfile.A2DP,
                                        BluetoothProfile.STATE_DISCONNECTED,
                                        BluetoothProfile.STATE_CONNECTING));
        Thread t2 =
                new Thread(
                        () ->
                                mScanManager.handleBluetoothProfileConnectionStateChanged(
                                        BluetoothProfile.HEADSET,
                                        BluetoothProfile.STATE_DISCONNECTED,
                                        BluetoothProfile.STATE_CONNECTING));

        // Connect 3 profiles concurrently.
        t1.start();
        t2.start();
        mScanManager.handleBluetoothProfileConnectionStateChanged(
                BluetoothProfile.HID_HOST,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_CONNECTING);

        t1.join();
        t2.join();
        TestUtils.waitForLooperToBeIdle(mHandler.getLooper());
        assertThat(mScanManager.mProfilesConnecting).isEqualTo(3);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ aconfig_declarations {
    name: "bluetooth_aconfig_flags",
    package: "com.android.bluetooth.flags",
    srcs: [
        "gatt.aconfig",
        "hap.aconfig",
        "hfp.aconfig",
        "system_service.aconfig",

flags/gatt.aconfig

0 → 100644
+8 −0
Original line number Diff line number Diff line
package: "com.android.bluetooth.flags"

flag {
    name: "enable_queuing_profile_connection_change"
    namespace: "bluetooth"
    description: "Queue Profile connection change events on the ClientHandler for Scan Manager"
    bug: "306033108"
}