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

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

Add wait-for-state command

Disable and enable are async shell command.
This is creating flakyness in test that expect the bluetooth to be
already enabled / disabled.

Test: atest BluetoothInstrumentationTests
Test: atest BluetoothShellCommandTest
Test: atest 'BluetoothTest#AdapterEnableDisable' // net_test_bluetooth
Fix: 261772749
Tag: #refactor
Change-Id: I3ca29dde23a5182ede28eceaf1ac97e4668ab5d5
parent a2a363f7
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -24,7 +24,8 @@
    </target_preparer>
    </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
    <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
    <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
    <option name="run-command" value="svc bluetooth disable" />
    <option name="run-command" value="cmd bluetooth_manager disable" />
    <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
  </target_preparer>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.FolderSaver">
  <target_preparer class="com.android.tradefed.targetprep.FolderSaver">
    <option name="device-path" value="/data/vendor/ssrdump" />
    <option name="device-path" value="/data/vendor/ssrdump" />
+2 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@
        <option name="throw-if-cmd-fail" value="true" />
        <option name="throw-if-cmd-fail" value="true" />
        <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
        <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
        <option name="run-command" value="cmd bluetooth_manager disable" />
        <option name="run-command" value="cmd bluetooth_manager disable" />
        <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
        <option name="run-command" value="setprop bluetooth.profile.hfp.hf.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.hfp.hf.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.pbap.client.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.pbap.client.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.map.client.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.map.client.enabled true" />
@@ -35,6 +36,7 @@
        <option name="run-command" value="setprop bluetooth.profile.a2dp.sink.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.a2dp.sink.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.sap.server.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.sap.server.enabled true" />
        <option name="teardown-command" value="cmd bluetooth_manager enable" />
        <option name="teardown-command" value="cmd bluetooth_manager enable" />
        <option name="teardown-command" value="cmd bluetooth_manager wait-for-state:STATE_ON" />
        <option name="teardown-command" value="settings put global ble_scan_always_enabled 1" />
        <option name="teardown-command" value="settings put global ble_scan_always_enabled 1" />
        <option name="teardown-command" value="setprop bluetooth.profile.hfp.hf.enabled false" />
        <option name="teardown-command" value="setprop bluetooth.profile.hfp.hf.enabled false" />
        <option name="teardown-command"
        <option name="teardown-command"
+2 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@
        <option name="throw-if-cmd-fail" value="true" />
        <option name="throw-if-cmd-fail" value="true" />
        <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
        <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
        <option name="run-command" value="cmd bluetooth_manager disable" />
        <option name="run-command" value="cmd bluetooth_manager disable" />
        <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
        <option name="run-command" value="setprop bluetooth.profile.hfp.hf.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.hfp.hf.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.pbap.client.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.pbap.client.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.map.client.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.map.client.enabled true" />
@@ -35,6 +36,7 @@
        <option name="run-command" value="setprop bluetooth.profile.a2dp.sink.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.a2dp.sink.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.sap.server.enabled true" />
        <option name="run-command" value="setprop bluetooth.profile.sap.server.enabled true" />
        <option name="teardown-command" value="cmd bluetooth_manager enable" />
        <option name="teardown-command" value="cmd bluetooth_manager enable" />
        <option name="teardown-command" value="cmd bluetooth_manager wait-for-state:STATE_ON" />
        <option name="teardown-command" value="settings put global ble_scan_always_enabled 1" />
        <option name="teardown-command" value="settings put global ble_scan_always_enabled 1" />
        <option name="teardown-command"
        <option name="teardown-command"
                value="setprop bluetooth.profile.pbap.client.enabled false" />
                value="setprop bluetooth.profile.pbap.client.enabled false" />
+16 −7
Original line number Original line Diff line number Diff line
@@ -2859,15 +2859,25 @@ public class BluetoothManagerService extends IBluetoothManager.Stub {
        }
        }
    }
    }


    boolean waitForManagerState(int state) {
        return waitForState(Set.of(state), false);
    }

    private boolean waitForState(Set<Integer> states) {
    private boolean waitForState(Set<Integer> states) {
        int i = 0;
        return waitForState(states, true);
        while (i < 10) {
    }
            try {
    private boolean waitForState(Set<Integer> states, boolean failIfUnbind) {
        for (int i = 0; i < 10; i++) {
            mBluetoothLock.readLock().lock();
            mBluetoothLock.readLock().lock();
                if (mBluetooth == null) {
            try {
                    break;
                if (mBluetooth == null && failIfUnbind) {
                    Log.e(TAG, "waitForState " + states + " Bluetooth is not unbind");
                    return false;
                }
                if (mBluetooth == null && states.contains(BluetoothAdapter.STATE_OFF)) {
                    return true; // We are so OFF that the bluetooth is not bind
                }
                }
                if (states.contains(synchronousGetState())) {
                if (mBluetooth != null && states.contains(synchronousGetState())) {
                    return true;
                    return true;
                }
                }
            } catch (RemoteException | TimeoutException e) {
            } catch (RemoteException | TimeoutException e) {
@@ -2877,7 +2887,6 @@ public class BluetoothManagerService extends IBluetoothManager.Stub {
                mBluetoothLock.readLock().unlock();
                mBluetoothLock.readLock().unlock();
            }
            }
            SystemClock.sleep(300);
            SystemClock.sleep(300);
            i++;
        }
        }
        Log.e(TAG, "waitForState " + states + " time out");
        Log.e(TAG, "waitForState " + states + " time out");
        return false;
        return false;
+119 −44
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package com.android.server.bluetooth;
package com.android.server.bluetooth;


import static java.util.Objects.requireNonNull;

import android.bluetooth.BluetoothAdapter;
import android.content.AttributionSource;
import android.content.AttributionSource;
import android.content.Context;
import android.content.Context;
import android.os.Binder;
import android.os.Binder;
@@ -29,7 +32,7 @@ import com.android.modules.utils.BasicShellCommandHandler;
import java.io.PrintWriter;
import java.io.PrintWriter;


class BluetoothShellCommand extends BasicShellCommandHandler {
class BluetoothShellCommand extends BasicShellCommandHandler {
    private static final String TAG = "BluetoothShellCommand";
    private static final String TAG = BluetoothShellCommand.class.getSimpleName();


    private final BluetoothManagerService mManagerService;
    private final BluetoothManagerService mManagerService;
    private final Context mContext;
    private final Context mContext;
@@ -38,49 +41,111 @@ class BluetoothShellCommand extends BasicShellCommandHandler {
    final BluetoothCommand[] mBluetoothCommands = {
    final BluetoothCommand[] mBluetoothCommands = {
        new Enable(),
        new Enable(),
        new Disable(),
        new Disable(),
        new WaitForAdapterState(),
    };
    };


    @VisibleForTesting
    @VisibleForTesting
    abstract class BluetoothCommand {
    abstract class BluetoothCommand {
        abstract String getName();
        final boolean mIsPrivileged;
        // require root permission by default, can be override in command implementation
        final String mName;

        BluetoothCommand(boolean isPrivileged, String name) {
            mIsPrivileged = isPrivileged;
            mName = requireNonNull(name, "Command name cannot be null");
        }

        String getName() {
            return mName;
        }
        boolean isMatch(String cmd) {
            return mName.equals(cmd);
        }
        boolean isPrivileged() {
        boolean isPrivileged() {
            return true;
            return mIsPrivileged;
        }
        }
        abstract int exec(PrintWriter pw) throws RemoteException;

        abstract int exec(String cmd) throws RemoteException;
        abstract void onHelp(PrintWriter pw);
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    class Enable extends BluetoothCommand {
    class Enable extends BluetoothCommand {
        @Override
        Enable() {
        String getName() {
            super(false, "enable");
            return "enable";
        }
        }
        @Override
        @Override
        boolean isPrivileged() {
        public int exec(String cmd) throws RemoteException {
            return false;
            return mManagerService.enable(AttributionSource.myAttributionSource()) ? 0 : -1;
        }
        }
        @Override
        @Override
        public int exec(PrintWriter pw) throws RemoteException {
        public void onHelp(PrintWriter pw) {
            pw.println("Enabling Bluetooth");
            pw.println("  " + getName());
            return mManagerService.enable(AttributionSource.myAttributionSource()) ? 0 : -1;
            pw.println("    Enable Bluetooth on this device.");
        }
        }
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    class Disable extends BluetoothCommand {
    class Disable extends BluetoothCommand {
        Disable() {
            super(false, "disable");
        }
        @Override
        @Override
        String getName() {
        public int exec(String cmd) throws RemoteException {
            return "disable";
            return mManagerService.disable(AttributionSource.myAttributionSource(), true) ? 0 : -1;
        }
        }
        @Override
        @Override
        boolean isPrivileged() {
        public void onHelp(PrintWriter pw) {
            return false;
            pw.println("  " + getName());
            pw.println("    Disable Bluetooth on this device.");
        }
    }

    @VisibleForTesting
    class WaitForAdapterState extends BluetoothCommand {
        WaitForAdapterState() {
            super(false, "wait-for-state");
        }
        private int getWaitingState(String in) {
            if (!in.startsWith(getName() + ":")) return -1;
            String[] split = in.split(":", 2);
            if (split.length != 2 || !getName().equals(split[0])) {
                String msg = getName() + ": Invalid state format: " + in;
                Log.e(TAG, msg);
                PrintWriter pw = getErrPrintWriter();
                pw.println(TAG + ": " + msg);
                printHelp(pw);
                throw new IllegalArgumentException();
            }
            switch (split[1]) {
                case "STATE_OFF":
                    return BluetoothAdapter.STATE_OFF;
                case "STATE_ON":
                    return BluetoothAdapter.STATE_ON;
                default:
                    String msg = getName() + ": Invalid state value: " + split[1] + ". From: " + in;
                    Log.e(TAG, msg);
                    PrintWriter pw = getErrPrintWriter();
                    pw.println(TAG + ": " + msg);
                    printHelp(pw);
                    throw new IllegalArgumentException();
            }
        }
        }
        @Override
        @Override
        public int exec(PrintWriter pw) throws RemoteException {
        boolean isMatch(String cmd) {
            pw.println("Disabling Bluetooth");
            return getWaitingState(cmd) != -1;
            return mManagerService.disable(AttributionSource.myAttributionSource(), true) ? 0 : -1;
        }
        @Override
        public int exec(String cmd) throws RemoteException {
            int ret = mManagerService.waitForManagerState(getWaitingState(cmd)) ? 0 : -1;
            Log.d(TAG, cmd + ": Return value is " + ret); // logging as this method can take time
            return ret;
        }
        @Override
        public void onHelp(PrintWriter pw) {
            pw.println("  " + getName() + ":<STATE>");
            pw.println("    Wait until the adapter state is <STATE>."
                    + " <STATE> can be one of STATE_OFF | STATE_ON");
            pw.println("    Note: This command can timeout and failed");
        }
        }
    }
    }


@@ -91,40 +156,50 @@ class BluetoothShellCommand extends BasicShellCommandHandler {


    @Override
    @Override
    public int onCommand(String cmd) {
    public int onCommand(String cmd) {
        if (cmd == null) {
        if (cmd == null) return handleDefaultCommands(null);
            return handleDefaultCommands(null);
        }


        for (BluetoothCommand bt_cmd : mBluetoothCommands) {
        for (BluetoothCommand bt_cmd : mBluetoothCommands) {
            if (cmd.equals(bt_cmd.getName())) {
            if (!bt_cmd.isMatch(cmd)) continue;
            if (bt_cmd.isPrivileged()) {
            if (bt_cmd.isPrivileged()) {
                final int uid = Binder.getCallingUid();
                final int uid = Binder.getCallingUid();
                if (uid != Process.ROOT_UID) {
                if (uid != Process.ROOT_UID) {
                    throw new SecurityException("Uid " + uid + " does not have access to "
                    throw new SecurityException("Uid " + uid + " does not have access to "
                                + cmd + " bluetooth command (or such command doesn't exist)");
                            + cmd + " bluetooth command");
                }
                }
            }
            }
            try {
            try {
                    return bt_cmd.exec(getOutPrintWriter());
                getOutPrintWriter().println(TAG + ": Exec" + cmd);
                Log.d(TAG, "Exec " + cmd);
                int ret = bt_cmd.exec(cmd);
                if (ret == 0) {
                    String msg = cmd + ": Success";
                    Log.d(TAG, msg);
                    getOutPrintWriter().println(msg);
                } else {
                    String msg = cmd + ": Failed with status=" + ret;
                    Log.e(TAG, msg);
                    getErrPrintWriter().println(TAG + ": " + msg);
                }
                return ret;
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Log.w(TAG, cmd + ": error\nException: " + e.getMessage());
                Log.w(TAG, cmd + ": error\nException: " + e.getMessage());
                getErrPrintWriter().println(cmd + ": error\nException: " + e.getMessage());
                getErrPrintWriter().println(cmd + ": error\nException: " + e.getMessage());
                e.rethrowFromSystemServer();
                e.rethrowFromSystemServer();
            }
            }
        }
        }
        }
        return handleDefaultCommands(cmd);
        return handleDefaultCommands(cmd);
    }
    }


    @Override
    private void printHelp(PrintWriter pw) {
    public void onHelp() {
        pw.println("Bluetooth Manager Commands:");
        PrintWriter pw = getOutPrintWriter();
        pw.println("Bluetooth Commands:");
        pw.println("  help or -h");
        pw.println("  help or -h");
        pw.println("    Print this help text.");
        pw.println("    Print this help text.");
        pw.println("  enable");
        for (BluetoothCommand bt_cmd : mBluetoothCommands) {
        pw.println("    Enable Bluetooth on this device.");
            bt_cmd.onHelp(pw);
        pw.println("  disable");
        }
        pw.println("    Disable Bluetooth on this device.");
    }
    @Override
    public void onHelp() {
        printHelp(getOutPrintWriter());
    }
    }
}
}
Loading