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

Commit b5458617 authored by Myles Watson's avatar Myles Watson
Browse files

AdapterService: Keep lists for profile states

Track ProfileService objects instead of strings
Document Profile functions
Reset Config.Init after AdapterServiceTest
Call initNative and cleanupNative in ProfileServiceTest

Bug: 72435402
Test: runtest bluetooth
Change-Id: I611ac68e55c5e5273aae3ea9f52342534cf00b83
parent b095b839
Loading
Loading
Loading
Loading
+122 −229
Original line number Diff line number Diff line
@@ -78,8 +78,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class AdapterService extends Service {
    private static final String TAG = "BluetoothAdapterService";
@@ -97,7 +95,8 @@ public class AdapterService extends Service {
    private long mEnergyUsedTotalVoltAmpSecMicro;
    private final SparseArray<UidTraffic> mUidTraffic = new SparseArray<>();

    private final ArrayList<ProfileService> mProfiles = new ArrayList<ProfileService>();
    private final ArrayList<ProfileService> mRegisteredProfiles = new ArrayList<>();
    private final ArrayList<ProfileService> mRunningProfiles = new ArrayList<>();

    public static final String ACTION_LOAD_ADAPTER_PROPERTIES =
            "com.android.bluetooth.btservice.action.LOAD_ADAPTER_PROPERTIES";
@@ -162,7 +161,6 @@ public class AdapterService extends Service {
    /* TODO: Consider to remove the search API from this class, if changed to use call-back */
    private SdpManager mSdpManager = null;

    private boolean mProfilesStarted;
    private boolean mNativeAvailable;
    private boolean mCleaningUp;
    private final HashMap<String, Integer> mProfileServicesState = new HashMap<String, Integer>();
@@ -183,129 +181,134 @@ public class AdapterService extends Service {
    private PhonePolicy mPhonePolicy;
    private ActiveDeviceManager mActiveDeviceManager;

    /**
     * Register a {@link ProfileService} with AdapterService.
     *
     * @param profile the service being added.
     */
    public void addProfile(ProfileService profile) {
        synchronized (mProfiles) {
            if (!mProfiles.contains(profile)) {
                mProfiles.add(profile);
            }
        }
        Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_REGISTERED, profile);
        mHandler.sendMessage(m);
    }

    /**
     * Unregister a ProfileService with AdapterService.
     *
     * @param profile the service being removed.
     */
    public void removeProfile(ProfileService profile) {
        synchronized (mProfiles) {
            mProfiles.remove(profile);
        }
        Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_UNREGISTERED, profile);
        mHandler.sendMessage(m);
    }

    public void onProfileServiceStateChanged(String serviceName, int state) {
    /**
     * Notify AdapterService that a ProfileService has started or stopped.
     *
     * @param profile the service being removed.
     * @param state {@link BluetoothAdapter#STATE_ON} or {@link BluetoothAdapter#STATE_OFF}
     */
    public void onProfileServiceStateChanged(ProfileService profile, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
            throw new IllegalArgumentException(BluetoothAdapter.nameForState(state));
        }
        Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
        m.obj = serviceName;
        m.obj = profile;
        m.arg1 = state;
        mHandler.sendMessage(m);
    }

    private void processProfileServiceStateChanged(String serviceName, int state) {
        boolean doUpdate = false;
        boolean isBleTurningOn;
        boolean isBleTurningOff;
        boolean isTurningOn;
        boolean isTurningOff;
    private static final int MESSAGE_PROFILE_SERVICE_STATE_CHANGED = 1;
    private static final int MESSAGE_PROFILE_SERVICE_REGISTERED = 2;
    private static final int MESSAGE_PROFILE_SERVICE_UNREGISTERED = 3;

        synchronized (mProfileServicesState) {
            Integer prevState = mProfileServicesState.get(serviceName);
            if (prevState != null && prevState != state) {
                mProfileServicesState.put(serviceName, state);
                doUpdate = true;
            }
        }
    class AdapterServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            debugLog("handleMessage() - Message: " + msg.what);

        if (!doUpdate) {
            return;
            switch (msg.what) {
                case MESSAGE_PROFILE_SERVICE_STATE_CHANGED:
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_STATE_CHANGED");
                    processProfileServiceStateChanged((ProfileService) msg.obj, msg.arg1);
                    break;
                case MESSAGE_PROFILE_SERVICE_REGISTERED:
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_REGISTERED");
                    registerProfileService((ProfileService) msg.obj);
                    break;
                case MESSAGE_PROFILE_SERVICE_UNREGISTERED:
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_UNREGISTERED");
                    unregisterProfileService((ProfileService) msg.obj);
                    break;
            }

        synchronized (mAdapterStateMachine) {
            isTurningOff = mAdapterStateMachine.isTurningOff();
            isTurningOn = mAdapterStateMachine.isTurningOn();
            isBleTurningOn = mAdapterStateMachine.isBleTurningOn();
            isBleTurningOff = mAdapterStateMachine.isBleTurningOff();
        }

        debugLog(
                "processProfileServiceStateChanged() - serviceName=" + serviceName + " isTurningOn="
                        + isTurningOn + " isTurningOff=" + isTurningOff + " isBleTurningOn="
                        + isBleTurningOn + " isBleTurningOff=" + isBleTurningOff);

        if (isBleTurningOn) {
            if (GattService.class.getName().equals(serviceName)) {
                debugLog("GattService is started");
                mAdapterStateMachine.sendMessage(
                        mAdapterStateMachine.obtainMessage(AdapterState.BLE_STARTED));
        private void registerProfileService(ProfileService profile) {
            if (mRegisteredProfiles.contains(profile)) {
                Log.e(TAG, profile.getName() + " already registered.");
                return;
            }
            mRegisteredProfiles.add(profile);
        }

        } else if (isBleTurningOff) {
            if (GattService.class.getName().equals(serviceName)) {
                debugLog("GattService stopped");
                mAdapterStateMachine.sendMessage(
                        mAdapterStateMachine.obtainMessage(AdapterState.BLE_STOPPED));
        private void unregisterProfileService(ProfileService profile) {
            if (!mRegisteredProfiles.contains(profile)) {
                Log.e(TAG, profile.getName() + " not registered (UNREGISTERED).");
                return;
            }

        } else if (isTurningOff) {
            //On to BLE_ON
            //Process stop or disable pending
            //Check if all services are stopped if so, do cleanup
            synchronized (mProfileServicesState) {
                Iterator<Map.Entry<String, Integer>> i =
                        mProfileServicesState.entrySet().iterator();
                while (i.hasNext()) {
                    Map.Entry<String, Integer> entry = i.next();
                    if (GattService.class.getName().equals(entry.getKey())) {
                        continue;
            mRegisteredProfiles.remove(profile);
        }

                    if (BluetoothAdapter.STATE_OFF != entry.getValue()) {
                        debugLog("onProfileServiceStateChange() - Profile still running: "
                                + entry.getKey());
        private void processProfileServiceStateChanged(ProfileService profile, int state) {
            switch (state) {
                case BluetoothAdapter.STATE_ON:
                    if (!mRegisteredProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not registered (STATE_ON).");
                        return;
                    }
                    if (mRunningProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " already running.");
                        return;
                    }
                    mRunningProfiles.add(profile);
                    if (GattService.class.getSimpleName().equals(profile.getName())) {
                        mAdapterStateMachine.sendMessage(
                                mAdapterStateMachine.obtainMessage(AdapterState.BLE_STARTED));
                    }
            debugLog("onProfileServiceStateChange() - All profile services stopped...");
            //Send message to state machine
            mProfilesStarted = false;
                    if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length
                            && mRegisteredProfiles.size() == mRunningProfiles.size()) {
                        mAdapterStateMachine.sendMessage(
                    mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STOPPED));

        } else if (isTurningOn) {
            updateInteropDatabase();

            //Process start pending
            //Check if all services are started if so, update state
            synchronized (mProfileServicesState) {
                Iterator<Map.Entry<String, Integer>> i =
                        mProfileServicesState.entrySet().iterator();
                while (i.hasNext()) {
                    Map.Entry<String, Integer> entry = i.next();
                    if (GattService.class.getName().equals(entry.getKey())) {
                        continue;
                                mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STARTED));
                    }

                    if (BluetoothAdapter.STATE_ON != entry.getValue()) {
                        debugLog(
                                "onProfileServiceStateChange() - Profile still not running:" + entry
                                        .getKey());
                    break;
                case BluetoothAdapter.STATE_OFF:
                    if (!mRegisteredProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not registered (STATE_OFF).");
                        return;
                    }
                    if (!mRunningProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not running.");
                        return;
                    }
                    mRunningProfiles.remove(profile);
                    // If the last profile was removed, or only GATT is left, send BREDR_STOPPED.
                    if ((mRunningProfiles.size() == 0) || (mRunningProfiles.size() == 1
                            && (GattService.class.getSimpleName()
                            .equals(mRunningProfiles.get(0).getName())))) {
                        mAdapterStateMachine.sendMessage(
                                mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STOPPED));
                    }
            debugLog("onProfileServiceStateChange() - All profile services started.");
            mProfilesStarted = true;
            //Send message to state machine
                    if (mRunningProfiles.size() == 0) {
                        mAdapterStateMachine.sendMessage(
                    mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STARTED));
                                mAdapterStateMachine.obtainMessage(AdapterState.BLE_STOPPED));
                    }
                    break;
                default:
                    Log.e(TAG, "Unhandled profile state: " + state);
            }
        }
    }

    private final AdapterServiceHandler mHandler = new AdapterServiceHandler();

    private void updateInteropDatabase() {
        interopDatabaseClearNative();
@@ -486,12 +489,6 @@ public class AdapterService extends Service {
            Config.init(getApplicationContext());
        }

        Class[] supportedProfileServices = Config.getSupportedProfiles();
        //Initialize data objects
        for (Class service : supportedProfileServices) {
            mProfileServicesState.put(service.getName(), BluetoothAdapter.STATE_OFF);
        }

        // Reset |mRemoteDevices| whenever BLE is turned off then on
        // This is to replace the fact that |mRemoteDevices| was
        // reinitialized in previous code.
@@ -512,11 +509,11 @@ public class AdapterService extends Service {
        try {
            mBatteryStats.noteResetBleScan();
        } catch (RemoteException e) {
            // Ignore.
            Log.w(TAG, "RemoteException trying to send a reset to BatteryStats");
        }

        //Start Gatt service
        setGattProfileServiceState(supportedProfileServices, BluetoothAdapter.STATE_ON);
        setProfileServiceState(GattService.class, BluetoothAdapter.STATE_ON);
    }

    /**
@@ -548,18 +545,7 @@ public class AdapterService extends Service {
    void startCoreServices() {
        debugLog("startCoreServices()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();

        //Start profile services
        if (!mProfilesStarted && supportedProfileServices.length > 0
                && !(supportedProfileServices.length == 1
                     && supportedProfileServices[0] == GattService.class)) {
            //Startup all profile services
            setProfileServiceState(supportedProfileServices, BluetoothAdapter.STATE_ON);
        } else {
            debugLog("startCoreProfiles(): Profile Services alreay started");
            mAdapterStateMachine.sendMessage(
                    mAdapterStateMachine.obtainMessage(AdapterState.BREDR_STARTED));
        }
        setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_ON);
    }

    void startBluetoothDisable() {
@@ -567,26 +553,20 @@ public class AdapterService extends Service {
                mAdapterStateMachine.obtainMessage(AdapterState.BEGIN_DISABLE));
    }

    boolean stopProfileServices() {
    void stopProfileServices() {
        Class[] supportedProfileServices = Config.getSupportedProfiles();
        if (mProfilesStarted && supportedProfileServices.length > 0) {
            setProfileServiceState(supportedProfileServices, BluetoothAdapter.STATE_OFF);
            return true;
        }
        if (mRunningProfiles.size() == 0) {
            debugLog("stopProfileServices() - No profiles services to stop or already stopped.");
        return false;
            return;
        }
        setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_OFF);
    }

    boolean stopGattProfileService() {
        //TODO: can optimize this instead of looping around all supported profiles
        debugLog("stopGattProfileService()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();

        setGattProfileServiceState(supportedProfileServices, BluetoothAdapter.STATE_OFF);
        setProfileServiceState(GattService.class, BluetoothAdapter.STATE_OFF);
        return true;
    }


    void updateAdapterState(int prevState, int newState) {
        if (mCallbacks != null) {
            int n = mCallbacks.beginBroadcast();
@@ -685,103 +665,19 @@ public class AdapterService extends Service {
        }
    }

    private static final int MESSAGE_PROFILE_SERVICE_STATE_CHANGED = 1;

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            debugLog("handleMessage() - Message: " + msg.what);

            switch (msg.what) {
                case MESSAGE_PROFILE_SERVICE_STATE_CHANGED: {
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_STATE_CHANGED");
                    processProfileServiceStateChanged((String) msg.obj, msg.arg1);
                }
                break;
            }
        }
    };

    @SuppressWarnings("rawtypes")
    private void setGattProfileServiceState(Class[] services, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
            Log.w(TAG, "setGattProfileServiceState(): invalid state...Leaving...");
            return;
        }

        int expectedCurrentState = BluetoothAdapter.STATE_OFF;
        int pendingState = BluetoothAdapter.STATE_TURNING_ON;

        if (state == BluetoothAdapter.STATE_OFF) {
            expectedCurrentState = BluetoothAdapter.STATE_ON;
            pendingState = BluetoothAdapter.STATE_TURNING_OFF;
        }

        for (Class service : services) {
            String serviceName = service.getName();
            String simpleName = service.getSimpleName();

            if (GattService.class.getSimpleName().equals(simpleName)) {
                Integer serviceState = mProfileServicesState.get(serviceName);

                if (serviceState != null && serviceState != expectedCurrentState) {
                    debugLog("setProfileServiceState() - Unable to " + (
                            state == BluetoothAdapter.STATE_OFF ? "start" : "stop") + " service "
                            + serviceName + ". Invalid state: " + serviceState);
                    continue;
                }
                debugLog("setProfileServiceState() - " + (state == BluetoothAdapter.STATE_OFF
                        ? "Stopping" : "Starting") + " service " + serviceName);

                mProfileServicesState.put(serviceName, pendingState);
    private void setProfileServiceState(Class service, int state) {
        Intent intent = new Intent(this, service);
        intent.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
        intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
        startService(intent);
                return;
            }
        }
    }


    @SuppressWarnings("rawtypes")
    private void setProfileServiceState(Class[] services, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
            debugLog("setProfileServiceState() - Invalid state, leaving...");
            return;
        }

        int expectedCurrentState = BluetoothAdapter.STATE_OFF;
        int pendingState = BluetoothAdapter.STATE_TURNING_ON;
        if (state == BluetoothAdapter.STATE_OFF) {
            expectedCurrentState = BluetoothAdapter.STATE_ON;
            pendingState = BluetoothAdapter.STATE_TURNING_OFF;
    }

    private void setAllProfileServiceStates(Class[] services, int state) {
        for (Class service : services) {
            String serviceName = service.getName();
            String simpleName = service.getSimpleName();

            if (GattService.class.getSimpleName().equals(simpleName)) {
                continue;
            }

            Integer serviceState = mProfileServicesState.get(serviceName);
            if (serviceState != null && serviceState != expectedCurrentState) {
                debugLog("setProfileServiceState() - Unable to " + (
                        state == BluetoothAdapter.STATE_OFF ? "start" : "stop") + " service "
                        + serviceName + ". Invalid state: " + serviceState);
            if (GattService.class.getSimpleName().equals(service.getSimpleName())) {
                continue;
            }

            debugLog("setProfileServiceState() - " + (state == BluetoothAdapter.STATE_OFF
                    ? "Stopping" : "Starting") + " service " + serviceName);

            mProfileServicesState.put(serviceName, pendingState);
            Intent intent = new Intent(this, service);
            intent.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
            intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
            startService(intent);
            setProfileServiceState(service, state);
        }
    }

@@ -2505,13 +2401,10 @@ public class AdapterService extends Service {
                            + device.getName());
        }

        // Dump profile information
        StringBuilder sb = new StringBuilder();
        synchronized (mProfiles) {
            for (ProfileService profile : mProfiles) {
        for (ProfileService profile : mRegisteredProfiles) {
            profile.dump(sb);
        }
        }

        writer.write(sb.toString());
        writer.flush();
@@ -2522,7 +2415,7 @@ public class AdapterService extends Service {
    private void dumpMetrics(FileDescriptor fd) {
        BluetoothProto.BluetoothLog metrics = new BluetoothProto.BluetoothLog();
        metrics.setNumBondedDevices(getBondedDevices().length);
        for (ProfileService profile : mProfiles) {
        for (ProfileService profile : mRegisteredProfiles) {
            profile.dumpProto(metrics);
        }
        byte[] nativeMetricsBytes = dumpMetricsNative();
+2 −4
Original line number Diff line number Diff line
@@ -268,8 +268,7 @@ public abstract class ProfileService extends Service {
            Log.e(mName, "Error starting profile. start() returned false.");
            return;
        }
        mAdapterService.onProfileServiceStateChanged(getClass().getName(),
                BluetoothAdapter.STATE_ON);
        mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON);
    }

    private void doStop() {
@@ -278,8 +277,7 @@ public abstract class ProfileService extends Service {
        }
        mProfileStarted = false;
        if (mAdapterService != null) {
            mAdapterService.onProfileServiceStateChanged(getClass().getName(),
                    BluetoothAdapter.STATE_OFF);
            mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_OFF);
        }
        if (!stop()) {
            Log.e(mName, "Unable to stop profile");
+7 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;

import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.util.MockUtil;

import java.lang.reflect.InvocationTargetException;
@@ -117,8 +118,10 @@ public class TestUtils {
                AdapterService.ACTION_SERVICE_STATE_CHANGED);
        startIntent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
        serviceTestRule.startService(startIntent);
        ArgumentCaptor<ProfileService> profile = ArgumentCaptor.forClass(profileServiceClass);
        verify(adapterService, timeout(SERVICE_TOGGLE_TIMEOUT_MS)).onProfileServiceStateChanged(
                eq(profileServiceClass.getName()), eq(BluetoothAdapter.STATE_ON));
                profile.capture(), eq(BluetoothAdapter.STATE_ON));
        Assert.assertEquals(profileServiceClass.getName(), profile.getValue().getClass().getName());
    }

    /**
@@ -146,8 +149,10 @@ public class TestUtils {
                AdapterService.ACTION_SERVICE_STATE_CHANGED);
        stopIntent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
        serviceTestRule.startService(stopIntent);
        ArgumentCaptor<ProfileService> profile = ArgumentCaptor.forClass(profileServiceClass);
        verify(adapterService, timeout(SERVICE_TOGGLE_TIMEOUT_MS)).onProfileServiceStateChanged(
                eq(profileServiceClass.getName()), eq(BluetoothAdapter.STATE_OFF));
                profile.capture(), eq(BluetoothAdapter.STATE_OFF));
        Assert.assertEquals(profileServiceClass.getName(), profile.getValue().getClass().getName());
    }

    /**
+50 −67

File changed.

Preview size limit exceeded, changes collapsed.

+45 −21

File changed.

Preview size limit exceeded, changes collapsed.