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

Commit 36c8630f authored by William Escande's avatar William Escande
Browse files

SystemServer: Airplane change when FactoryReset

When receiving ACTION_SETTING_RESTORED we temporarily set the persisted
state to off.
If we leave airplane mode before the restore is executed we will have a
race condition on the start of the bluetooth.
This CL adds a check on the persistedState, to make sure we do not
wrongly interact with it

Bug: 328842491
Fix: 328842491
Test: atest ServiceBluetoothTests
Test: atest disableAirplane_whenFactoryReset_doesNotStartBluetooth
Test: atest disableAirplane_whenNothing_startBluetooth
Flag: Exempt, Adding a flag here does not add DF value as it is a very
      edge case (you need to stress test factory reset & airplane mode)
Change-Id: I969e156c9f289aa0088887d8c7fafca15a4bc843
parent 1a279f97
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -142,7 +142,7 @@ class BluetoothManagerService {
    // Delay for the addProxy function in msec
    private static final int ADD_PROXY_DELAY_MS = 100 * HW_MULTIPLIER;
    // Delay for retrying enable and disable in msec
    private static final int ENABLE_DISABLE_DELAY_MS = 300 * HW_MULTIPLIER;
    @VisibleForTesting static final int ENABLE_DISABLE_DELAY_MS = 300 * HW_MULTIPLIER;

    @VisibleForTesting static final int MESSAGE_ENABLE = 1;
    @VisibleForTesting static final int MESSAGE_DISABLE = 2;
@@ -182,7 +182,7 @@ class BluetoothManagerService {
    private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks =
            new RemoteCallbackList<>();
    private final BluetoothServiceBinder mBinder;
    private final BluetoothHandler mHandler;
    @VisibleForTesting final BluetoothHandler mHandler;
    private final ContentResolver mContentResolver;
    private final Context mContext;
    private final Looper mLooper;
@@ -469,7 +469,8 @@ class BluetoothManagerService {

        if (isAirplaneModeOn) {
            forceToOffFromModeChange(currentState, ENABLE_DISABLE_REASON_AIRPLANE_MODE);
        } else if (mEnableExternal) {
        } else if (mEnableExternal && currentState != STATE_ON && isPersistStateOn) {
            // isPersistStateOn is checked to prevent race with RESTORE_USER_SETTING
            sendEnableMsg(mQuietEnableExternal, ENABLE_DISABLE_REASON_AIRPLANE_MODE);
        } else if (currentState != STATE_ON) {
            autoOnSetupTimer();
+74 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.bluetooth;
import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;

import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_BLUETOOTH_SERVICE_CONNECTED;
@@ -26,7 +27,9 @@ import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_BLUET
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_BLUETOOTH_STATE_CHANGE;
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_DISABLE;
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_ENABLE;
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_HANDLE_DISABLE_DELAYED;
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_RESTART_BLUETOOTH_SERVICE;
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_RESTORE_USER_SETTING_OFF;
import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_TIMEOUT_BIND;

import static com.google.common.truth.Truth.assertThat;
@@ -101,6 +104,7 @@ public class BluetoothManagerServiceTest {
    }

    private static final int STATE_BLE_TURNING_ON = 14; // can't find the symbol because hidden api
    private static final int STATE_BLE_TURNING_OFF = 16; // can't find the symbol because hidden api

    BluetoothManagerService mManagerService;

@@ -185,6 +189,7 @@ public class BluetoothManagerServiceTest {

        doReturn(mAdapterBinder).when(mBluetoothServerProxy).createAdapterBinder(any());
        doReturn(mAdapterService).when(mAdapterBinder).getAdapterBinder();
        doReturn(mBinder).when(mAdapterService).asBinder();

        doReturn(mock(Intent.class))
                .when(mContext)
@@ -238,6 +243,17 @@ public class BluetoothManagerServiceTest {
                        });
    }

    private void discardMessage(int... what) {
        IntStream.of(what)
                .forEach(
                        w -> {
                            Message msg = mLooper.nextMessage();
                            assertThat(msg).isNotNull();
                            assertThat(msg.what).isEqualTo(w);
                            // Drop the message
                        });
    }

    @Test
    public void onUserRestrictionsChanged_disallowBluetooth_onlySendDisableMessageOnSystemUser()
            throws InterruptedException {
@@ -358,6 +374,22 @@ public class BluetoothManagerServiceTest {
        return btCallback;
    }

    private void transition_onToBleOn(IBluetoothCallback btCallback) throws Exception {
        verify(mAdapterBinder).onToBleOn(any());

        btCallback.onBluetoothStateChange(STATE_TURNING_OFF, STATE_BLE_ON);
        syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE);
    }

    private void transition_onToOff(IBluetoothCallback btCallback) throws Exception {
        transition_onToBleOn(btCallback);
        verify(mAdapterBinder).bleOnToOff(any());

        // When all the profile are started, adapterService consider it is ON
        btCallback.onBluetoothStateChange(STATE_BLE_TURNING_OFF, STATE_OFF);
        syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE);
    }

    @Test
    public void enable_whileTurningToBleOn_shouldEnable() throws Exception {
        mManagerService.enableBle("enable_whileTurningToBleOn_shouldEnable", mBinder);
@@ -450,9 +482,47 @@ public class BluetoothManagerServiceTest {
        assertThat(mManagerService.getState()).isEqualTo(STATE_OFF);

        mLooper.moveTimeForward(120_000);
        Message msg = mLooper.nextMessage();
        assertThat(msg).isNotNull();
        assertThat(msg.what).isEqualTo(MESSAGE_RESTART_BLUETOOTH_SERVICE);
        // Discard the msg without executing it
        discardMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
    }

    @Test
    public void disableAirplane_whenNothing_startBluetooth() throws Exception {
        doReturn(BluetoothManagerService.BLUETOOTH_ON_BLUETOOTH)
                .when(mBluetoothServerProxy)
                .getBluetoothPersistedState(any(), anyInt());
        mManagerService.enable("disableAirplane_whenNothing_startBluetooth");
        discardMessage(MESSAGE_ENABLE);

        mManagerService.onAirplaneModeChanged(false);
        discardMessage(MESSAGE_ENABLE);
    }

    @Test
    public void disableAirplane_whenFactoryReset_doesNotStartBluetooth() throws Exception {
        doAnswer(
                        invocation -> {
                            IBinder.DeathRecipient recipient = invocation.getArgument(0);
                            recipient.binderDied();
                            return null;
                        })
                .when(mBinder)
                .linkToDeath(any(), anyInt());

        mManagerService.enable("test_offToOn");
        syncHandler(MESSAGE_ENABLE);
        IBluetoothCallback btCallback = transition_offToOn();
        assertThat(mManagerService.getState()).isEqualTo(STATE_ON);

        mManagerService.mHandler.sendEmptyMessage(MESSAGE_RESTORE_USER_SETTING_OFF);
        syncHandler(MESSAGE_RESTORE_USER_SETTING_OFF);
        syncHandler(MESSAGE_DISABLE);
        mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS);
        syncHandler(MESSAGE_HANDLE_DISABLE_DELAYED);
        mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS);
        syncHandler(MESSAGE_HANDLE_DISABLE_DELAYED);
        transition_onToOff(btCallback);

        mManagerService.onAirplaneModeChanged(false);
        assertThat(mLooper.nextMessage()).isNull(); // Must not create a MESSAGE_ENABLE
    }
}