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

Commit 292bc3c6 authored by Myles Watson's avatar Myles Watson
Browse files

AdapterState: Simplify state transitions

- Split PendingCommandState into
  - TurningOnState
  - TurningOffState
  - TurningBleOnState
  - TurningBleOffState
- Move timeouts to enter() and exit()
- Move the native callback to AdapterService
- Remove scan setting disable

Bug: 72435402
Fixes: 66452171
Fixes: 66711270
Test: runtest bluetooth
      Toggle bluetooth from settings rapidly
      act.py -tc BtStressTest:test_toggle_bluetooth
Change-Id: Ic9728a9717dac7b337978deeb58104b38fea641e
parent 9a823259
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -744,10 +744,6 @@ class AdapterProperties {
                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                        mService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
                        debugLog("Scan Mode:" + mScanMode);
                        if (mBluetoothDisabling) {
                            mBluetoothDisabling = false;
                            mService.startBluetoothDisable();
                        }
                        break;
                    case AbstractionLayer.BT_PROPERTY_UUIDS:
                        mUuids = Utils.byteArrayToUuid(val);
@@ -842,8 +838,6 @@ class AdapterProperties {
        }
    }

    private boolean mBluetoothDisabling = false;

    void onBleDisable() {
        // Sequence BLE_ON to STATE_OFF - that is _complete_ OFF state.
        // When BT disable is invoked, set the scan_mode to NONE
@@ -862,7 +856,6 @@ class AdapterProperties {
        //Set flag to indicate we are disabling. When property change of scan mode done
        //continue with disable sequence
        debugLog("onBluetoothDisable()");
        mBluetoothDisabling = true;
        if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
            // Turn off any Device Search/Inquiry
            mService.cancelDiscovery();
+48 −18
Original line number Diff line number Diff line
@@ -270,9 +270,11 @@ public class AdapterService extends Service {
                    }
                    mRunningProfiles.add(profile);
                    if (GattService.class.getSimpleName().equals(profile.getName())) {
                        mAdapterStateMachine.sendMessage(AdapterState.BLE_STARTED);
                        enableNativeWithGuestFlag();
                    } else if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length
                            && mRegisteredProfiles.size() == mRunningProfiles.size()) {
                        updateUuids();
                        setBluetoothClassFromConfig();
                        mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);
                    }
                    break;
@@ -291,6 +293,7 @@ public class AdapterService extends Service {
                            .equals(mRunningProfiles.get(0).getName())))) {
                        mAdapterStateMachine.sendMessage(AdapterState.BREDR_STOPPED);
                    } else if (mRunningProfiles.size() == 0) {
                        disableNative();
                        mAdapterStateMachine.sendMessage(AdapterState.BLE_STOPPED);
                    }
                    break;
@@ -369,8 +372,8 @@ public class AdapterService extends Service {
        mRemoteDevices.init();
        mBinder = new AdapterServiceBinder(this);
        mAdapterProperties = new AdapterProperties(this);
        mAdapterStateMachine = AdapterState.make(this, mAdapterProperties);
        mJniCallbacks = new JniCallbacks(mAdapterStateMachine, mAdapterProperties);
        mAdapterStateMachine = AdapterState.make(this);
        mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
        initNative();
        mNativeAvailable = true;
        mCallbacks = new RemoteCallbackList<IBluetoothCallback>();
@@ -473,7 +476,7 @@ public class AdapterService extends Service {
        }
    };

    void bleOnProcessStart() {
    void bringUpBle() {
        debugLog("bleOnProcessStart()");

        if (getResources().getBoolean(
@@ -508,6 +511,20 @@ public class AdapterService extends Service {
        setProfileServiceState(GattService.class, BluetoothAdapter.STATE_ON);
    }

    void bringDownBle() {
        stopGattProfileService();
    }

    void stateChangeCallback(int status) {
        if (status == AbstractionLayer.BT_STATE_OFF) {
            debugLog("stateChangeCallback: disableNative() completed");
        } else if (status == AbstractionLayer.BT_STATE_ON) {
            mAdapterStateMachine.sendMessage(AdapterState.BLE_STARTED);
        } else {
            Log.e(TAG, "Incorrect status " + status + " in stateChangeCallback");
        }
    }

    /**
     * Sets the Bluetooth CoD value of the local adapter if there exists a config value for it.
     */
@@ -534,31 +551,34 @@ public class AdapterService extends Service {
        return result;
    }

    void startCoreServices() {
    void startProfileServices() {
        debugLog("startCoreServices()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();
        setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_ON);
    }

    void startBluetoothDisable() {
        mAdapterStateMachine.sendMessage(AdapterState.BEGIN_DISABLE);
    }

    void stopProfileServices() {
        mAdapterProperties.onBluetoothDisable();
        Class[] supportedProfileServices = Config.getSupportedProfiles();
        if (mRunningProfiles.size() == 0) {
        if (mRunningProfiles.size() < 1 || (mRunningProfiles.size() == 1
                && GattService.class.getSimpleName().equals(mRunningProfiles.get(0).getName()))) {
            debugLog("stopProfileServices() - No profiles services to stop or already stopped.");
            return;
            mAdapterStateMachine.sendMessage(AdapterState.BREDR_STOPPED);
        }
        setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_OFF);
    }

    boolean stopGattProfileService() {
    private void stopGattProfileService() {
        mAdapterProperties.onBleDisable();
        if (mRunningProfiles.size() == 0) {
            debugLog("stopGattProfileService() - No profiles services to stop.");
            mAdapterStateMachine.sendMessage(AdapterState.BLE_STOPPED);
        }
        setProfileServiceState(GattService.class, BluetoothAdapter.STATE_OFF);
        return true;
    }

    void updateAdapterState(int prevState, int newState) {
        mAdapterProperties.setState(newState);
        if (mCallbacks != null) {
            int n = mCallbacks.beginBroadcast();
            debugLog("updateAdapterState() - Broadcasting state " + BluetoothAdapter.nameForState(
@@ -1576,7 +1596,7 @@ public class AdapterService extends Service {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");

        debugLog("disable() called with mRunningProfiles.size() = " + mRunningProfiles.size());
        mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_OFF);
        mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_OFF);
        return true;
    }

@@ -2194,12 +2214,12 @@ public class AdapterService extends Service {
        return mAdapterProperties.getTotalNumOfTrackableAdvertisements();
    }

    public void onLeServiceUp() {
    void onLeServiceUp() {
        mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_ON);
    }

    public void onBrEdrDown() {
        mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_OFF);
    void onBrEdrDown() {
        mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_OFF);
    }

    private static int convertScanModeToHal(int mode) {
@@ -2393,6 +2413,9 @@ public class AdapterService extends Service {
                            + device.getName());
        }

        writer.println();
        mAdapterStateMachine.dump(fd, writer, args);

        StringBuilder sb = new StringBuilder();
        for (ProfileService profile : mRegisteredProfiles) {
            profile.dump(sb);
@@ -2456,7 +2479,14 @@ public class AdapterService extends Service {
        }
    };

    private static native void classInitNative();
    private void enableNativeWithGuestFlag() {
        boolean isGuest = UserManager.get(this).isGuestUser();
        if (!enableNative(isGuest)) {
            Log.e(TAG, "enableNative() returned false");
        }
    }

    static native void classInitNative();

    native boolean initNative();

+227 −402

File changed.

Preview size limit exceeded, changes collapsed.

+5 −5
Original line number Diff line number Diff line
@@ -20,11 +20,11 @@ final class JniCallbacks {

    private RemoteDevices mRemoteDevices;
    private AdapterProperties mAdapterProperties;
    private AdapterState mAdapterStateMachine;
    private AdapterService mAdapterService;
    private BondStateMachine mBondStateMachine;

    JniCallbacks(AdapterState adapterStateMachine, AdapterProperties adapterProperties) {
        mAdapterStateMachine = adapterStateMachine;
    JniCallbacks(AdapterService adapterService, AdapterProperties adapterProperties) {
        mAdapterService = adapterService;
        mAdapterProperties = adapterProperties;
    }

@@ -36,7 +36,7 @@ final class JniCallbacks {
    void cleanup() {
        mRemoteDevices = null;
        mAdapterProperties = null;
        mAdapterStateMachine = null;
        mAdapterService = null;
        mBondStateMachine = null;
    }

@@ -70,7 +70,7 @@ final class JniCallbacks {
    }

    void stateChangeCallback(int status) {
        mAdapterStateMachine.stateChangeCallback(status);
        mAdapterService.stateChangeCallback(status);
    }

    void discoveryStateChangeCallback(int state) {
+151 −4
Original line number Diff line number Diff line
@@ -71,6 +71,10 @@ public class AdapterServiceTest {
    private static final int ONE_SECOND_MS = 1000;
    private static final int NATIVE_INIT_MS = 8000;

    private PowerManager mPowerManager;
    private PackageManager mMockPackageManager;
    private MockContentResolver mMockContentResolver;

    @Before
    public void setUp() throws PackageManager.NameNotFoundException {
        if (Looper.myLooper() == null) {
@@ -84,10 +88,10 @@ public class AdapterServiceTest {
                mAdapterService = new AdapterService();
            }
        });
        PackageManager mMockPackageManager = mock(PackageManager.class);
        MockContentResolver mMockContentResolver = new MockContentResolver(mMockContext);
        mMockPackageManager = mock(PackageManager.class);
        mMockContentResolver = new MockContentResolver(mMockContext);
        MockitoAnnotations.initMocks(this);
        PowerManager powerManager = (PowerManager) InstrumentationRegistry.getTargetContext()
        mPowerManager = (PowerManager) InstrumentationRegistry.getTargetContext()
                .getSystemService(Context.POWER_SERVICE);

        when(mMockContext.getApplicationInfo()).thenReturn(mMockApplicationInfo);
@@ -97,7 +101,7 @@ public class AdapterServiceTest {
        when(mMockContext.getUserId()).thenReturn(Process.BLUETOOTH_UID);
        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
        when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
        when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
        when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
        when(mMockContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mMockAlarmManager);

        when(mMockResources.getBoolean(R.bool.profile_supported_gatt)).thenReturn(true);
@@ -225,4 +229,147 @@ public class AdapterServiceTest {
        doEnable(0);
        doDisable(0);
    }

    /**
     * Test: Don't start GATT
     * Check whether the AdapterService quits gracefully
     */
    @Test
    public void testGattStartTimeout() {
        Assert.assertFalse(mAdapterService.isEnabled());

        mAdapterService.enable();

        verifyStateChange(BluetoothAdapter.STATE_OFF, BluetoothAdapter.STATE_BLE_TURNING_ON, 1,
                CONTEXT_SWITCH_MS);

        // Start GATT
        verify(mMockContext, timeout(CONTEXT_SWITCH_MS).times(1)).startService(any());
        mAdapterService.addProfile(mMockGattService);

        verifyStateChange(BluetoothAdapter.STATE_BLE_TURNING_ON,
                BluetoothAdapter.STATE_BLE_TURNING_OFF, 1,
                AdapterState.BLE_START_TIMEOUT_DELAY + CONTEXT_SWITCH_MS);

        // Stop GATT
        verify(mMockContext, timeout(AdapterState.BLE_STOP_TIMEOUT_DELAY + CONTEXT_SWITCH_MS)
                .times(2)).startService(any());

        verifyStateChange(BluetoothAdapter.STATE_BLE_TURNING_OFF, BluetoothAdapter.STATE_OFF, 1,
                CONTEXT_SWITCH_MS);

        Assert.assertFalse(mAdapterService.isEnabled());
    }

    /**
     * Test: Don't stop GATT
     * Check whether the AdapterService quits gracefully
     */
    @Test
    public void testGattStopTimeout() {
        doEnable(0);
        Assert.assertTrue(mAdapterService.isEnabled());

        mAdapterService.disable();

        verifyStateChange(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF, 1,
                CONTEXT_SWITCH_MS);

        // Stop PBAP and PAN
        verify(mMockContext, timeout(ONE_SECOND_MS).times(5)).startService(any());
        mAdapterService.onProfileServiceStateChanged(mMockService, BluetoothAdapter.STATE_OFF);
        mAdapterService.onProfileServiceStateChanged(mMockService2, BluetoothAdapter.STATE_OFF);

        verifyStateChange(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_BLE_ON, 1,
                CONTEXT_SWITCH_MS);

        mAdapterService.onBrEdrDown();

        verifyStateChange(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_BLE_TURNING_OFF, 1,
                CONTEXT_SWITCH_MS);

        // Stop GATT
        verify(mMockContext, timeout(ONE_SECOND_MS).times(6)).startService(any());

        verifyStateChange(BluetoothAdapter.STATE_BLE_TURNING_OFF, BluetoothAdapter.STATE_OFF, 1,
                AdapterState.BLE_STOP_TIMEOUT_DELAY + CONTEXT_SWITCH_MS);

        Assert.assertFalse(mAdapterService.isEnabled());
    }

    /**
     * Test: Don't start a classic profile
     * Check whether the AdapterService quits gracefully
     */
    @Test
    public void testProfileStartTimeout() {
        Assert.assertFalse(mAdapterService.isEnabled());

        mAdapterService.enable();

        verifyStateChange(BluetoothAdapter.STATE_OFF, BluetoothAdapter.STATE_BLE_TURNING_ON, 1,
                CONTEXT_SWITCH_MS);

        // Start GATT
        verify(mMockContext, timeout(CONTEXT_SWITCH_MS).times(1)).startService(any());
        mAdapterService.addProfile(mMockGattService);
        mAdapterService.onProfileServiceStateChanged(mMockGattService, BluetoothAdapter.STATE_ON);

        verifyStateChange(BluetoothAdapter.STATE_BLE_TURNING_ON, BluetoothAdapter.STATE_BLE_ON, 1,
                NATIVE_INIT_MS);

        mAdapterService.onLeServiceUp();

        verifyStateChange(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_TURNING_ON, 1,
                CONTEXT_SWITCH_MS);

        // Register Mock PBAP and PAN services, only start one
        verify(mMockContext, timeout(ONE_SECOND_MS).times(3)).startService(any());
        mAdapterService.addProfile(mMockService);
        mAdapterService.addProfile(mMockService2);
        mAdapterService.onProfileServiceStateChanged(mMockService, BluetoothAdapter.STATE_ON);

        verifyStateChange(BluetoothAdapter.STATE_TURNING_ON, BluetoothAdapter.STATE_TURNING_OFF, 1,
                AdapterState.BREDR_START_TIMEOUT_DELAY + CONTEXT_SWITCH_MS);

        // Stop PBAP and PAN
        verify(mMockContext, timeout(ONE_SECOND_MS).times(5)).startService(any());
        mAdapterService.onProfileServiceStateChanged(mMockService, BluetoothAdapter.STATE_OFF);

        verifyStateChange(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_BLE_ON, 1,
                CONTEXT_SWITCH_MS);
    }

    /**
     * Test: Don't stop a classic profile
     * Check whether the AdapterService quits gracefully
     */
    @Test
    public void testProfileStopTimeout() {
        doEnable(0);

        Assert.assertTrue(mAdapterService.isEnabled());

        mAdapterService.disable();

        verifyStateChange(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF, 1,
                CONTEXT_SWITCH_MS);

        // Stop PBAP and PAN
        verify(mMockContext, timeout(ONE_SECOND_MS).times(5)).startService(any());
        mAdapterService.onProfileServiceStateChanged(mMockService, BluetoothAdapter.STATE_OFF);

        verifyStateChange(BluetoothAdapter.STATE_TURNING_OFF,
                BluetoothAdapter.STATE_BLE_TURNING_OFF, 1,
                AdapterState.BREDR_STOP_TIMEOUT_DELAY + CONTEXT_SWITCH_MS);

        // Stop GATT
        verify(mMockContext, timeout(ONE_SECOND_MS).times(6)).startService(any());
        mAdapterService.onProfileServiceStateChanged(mMockGattService, BluetoothAdapter.STATE_OFF);

        verifyStateChange(BluetoothAdapter.STATE_BLE_TURNING_OFF, BluetoothAdapter.STATE_OFF, 1,
                AdapterState.BLE_STOP_TIMEOUT_DELAY + CONTEXT_SWITCH_MS);

        Assert.assertFalse(mAdapterService.isEnabled());
    }
}