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

Commit c967f125 authored by William Escande's avatar William Escande
Browse files

Adapter Lifecycle: receive force kill when stuck

Bug: 339548431
Bug: 336514848
Bug: 339548431
Flag: com.android.bluetooth.flags.explicit_kill_from_system_server
Test: None
Change-Id: Iecb232a9ae48e7c6a256ef11e4e017bbff304c30
parent 41fff951
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ import com.android.bluetooth.telephony.BluetoothInCallService;
import com.android.bluetooth.vc.VolumeControlService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.expresslog.Counter;
import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.BytesMatcher;

@@ -2314,8 +2315,7 @@ public class AdapterService extends Service {
            mService.enforceCallingPermission(
                    android.Manifest.permission.BLUETOOTH_PRIVILEGED, null);

            // Post on the main handler to be sure the cleanup has completed before calling exit
            mService.mHandler.post(
            Runnable killAction =
                    () -> {
                        if (Flags.killInsteadOfExit()) {
                            Log.i(TAG, "killBluetoothProcess: Calling killProcess(myPid())");
@@ -2324,7 +2324,24 @@ public class AdapterService extends Service {
                            Log.i(TAG, "killBluetoothProcess: Calling System.exit");
                            System.exit(0);
                        }
                    });
                    };

            // Post on the main handler to let the cleanup complete before calling exit
            mService.mHandler.post(killAction);

            try {
                // Wait for Bluetooth to be killed from its main thread
                Thread.sleep(950); // SystemServer is waiting 1000 ms, we need to wait less here
            } catch (InterruptedException e) {
                Log.e(TAG, "killBluetoothProcess: Interrupted while waiting for kill");
            }

            // Bluetooth cannot be killed on the main thread; it is in a deadLock.
            // Trying to recover by killing the Bluetooth from the binder thread.
            // This is bad :(
            Counter.logIncrement("bluetooth.value_kill_from_binder_thread");
            Log.wtf(TAG, "Failed to kill Bluetooth using its main thread. Trying from binder");
            killAction.run();
        }

        @Override
+48 −43
Original line number Diff line number Diff line
@@ -1244,10 +1244,12 @@ class BluetoothManagerService {
        mAdapterLock.writeLock().lock();
        try {
            mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
            if (mAdapter != null) {
            if (mAdapter == null) {
                return;
            }

            try {
                    mAdapter.unregisterCallback(
                            mBluetoothCallback, mContext.getAttributionSource());
                mAdapter.unregisterCallback(mBluetoothCallback, mContext.getAttributionSource());
            } catch (RemoteException e) {
                Log.e(TAG, "Unable to unregister BluetoothCallback", e);
            }
@@ -1263,36 +1265,39 @@ class BluetoothManagerService {
            try {
                mAdapter.getAdapterBinder()
                        .asBinder()
                            .linkToDeath(() -> binderDead.complete(null), 0);
                        .linkToDeath(
                                () -> {
                                    Log.i(TAG, "Successfully received Bluetooth death");
                                    binderDead.complete(null);
                                },
                                0);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to linkToDeath", e);
                binderDead.complete(null);
            }

                // Unbind first to avoid receiving "onServiceDisconnected"
            // Unbind first to avoid receiving unwanted "onServiceDisconnected"
            mContext.unbindService(mConnection);

            try {
                    // Force kill the bluetooth to make sure the process is not reused.
                    // Note that in a perfect world, we should be able to re-init the same process.
                    // Unfortunately, this require an heavy rework of the shutdown implementation
                // Force kill Bluetooth to make sure its process is not reused.
                // Note: In a perfect world, we should be able to re-init the same process.
                // Unfortunately, this requires an heavy rework of the Bluetooth app
                // TODO: b/339501753 - Properly stop Bluetooth without killing it
                mAdapter.killBluetoothProcess();

                    // if the kill throw, skip waiting as there is no bluetooth to wait for
                binderDead.get(1, TimeUnit.SECONDS);
            } catch (android.os.DeadObjectException e) {
                    // Reduce error -> info since Bluetooth may already be dead prior to this call
                // Reduce exception to info and skip waiting (Bluetooth is dead as wanted)
                Log.i(TAG, "Bluetooth already dead 💀");
            } catch (RemoteException e) {
                    Log.e(TAG, "Unexpected error when calling killBluetoothProcess", e);
                Log.e(TAG, "Unexpected RemoteException when calling killBluetoothProcess", e);
            } catch (TimeoutException | InterruptedException | ExecutionException e) {
                    Log.e(TAG, "Bluetooth death not received in time", e);
                Log.e(TAG, "Bluetooth death not received correctly", e);
            }

            mAdapter = null;
            mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
            }
        } finally {
            mAdapterLock.writeLock().unlock();
        }