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

Commit 11f2a35d authored by David Duarte's avatar David Duarte Committed by Gerrit Code Review
Browse files

Merge "Add BluetoothProfileConnectorTest" into main

parents b3d8a8d9 9d2d4e22
Loading
Loading
Loading
Loading
+45 −27
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;

import java.util.Objects;

/**
 * Connector for Bluetooth profile proxies to bind manager service and
 * profile services
@@ -41,9 +43,10 @@ public abstract class BluetoothProfileConnector<T> {
    private final int mProfileId;
    private BluetoothProfile.ServiceListener mServiceListener;
    private final BluetoothProfile mProfileProxy;
    private Context mContext;
    private String mPackageName;
    private final String mProfileName;
    private final String mServiceName;
    private final IBluetoothManager mBluetoothManager;
    private volatile T mService;

    private static final int MESSAGE_SERVICE_CONNECTED = 100;
@@ -79,12 +82,28 @@ public abstract class BluetoothProfileConnector<T> {
        }
    };

    BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
            String serviceName) {
    /** @hide */
    public BluetoothProfileConnector(
            BluetoothProfile profile,
            int profileId,
            String profileName,
            String serviceName,
            IBluetoothManager bluetoothManager) {
        mProfileId = profileId;
        mProfileProxy = profile;
        mProfileName = profileName;
        mServiceName = serviceName;
        mBluetoothManager = Objects.requireNonNull(bluetoothManager);
    }

    BluetoothProfileConnector(
            BluetoothProfile profile, int profileId, String profileName, String serviceName) {
        this(
                profile,
                profileId,
                profileName,
                serviceName,
                BluetoothAdapter.getDefaultAdapter().getBluetoothManager());
    }

    /** {@hide} */
@@ -97,11 +116,11 @@ public abstract class BluetoothProfileConnector<T> {
    private boolean doBind() {
        synchronized (mConnection) {
            if (mService == null) {
                logDebug("Binding service for " + mContext.getPackageName());
                logDebug("Binding service for " + mPackageName);
                mCloseGuard.open("doUnbind");
                try {
                    return BluetoothAdapter.getDefaultAdapter().getBluetoothManager()
                            .bindBluetoothProfileService(mProfileId, mServiceName, mConnection);
                    return mBluetoothManager.bindBluetoothProfileService(
                            mProfileId, mServiceName, mConnection);
                } catch (RemoteException re) {
                    logError("Failed to bind service. " + re);
                    return false;
@@ -114,11 +133,10 @@ public abstract class BluetoothProfileConnector<T> {
    private void doUnbind() {
        synchronized (mConnection) {
            if (mService != null) {
                logDebug("Unbinding service for " + mContext.getPackageName());
                logDebug("Unbinding service for " + mPackageName);
                mCloseGuard.close();
                try {
                    BluetoothAdapter.getDefaultAdapter().getBluetoothManager()
                            .unbindBluetoothProfileService(mProfileId, mConnection);
                    mBluetoothManager.unbindBluetoothProfileService(mProfileId, mConnection);
                } catch (RemoteException re) {
                    logError("Unable to unbind service: " + re);
                } finally {
@@ -129,10 +147,6 @@ public abstract class BluetoothProfileConnector<T> {
    }

    void connect(Context context, BluetoothProfile.ServiceListener listener) {
        mContext = context;
        mServiceListener = listener;
        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();

        // Preserve legacy compatibility where apps were depending on
        // registerStateChangeCallback() performing a permissions check which
        // has been relaxed in modern platform versions
@@ -142,30 +156,34 @@ public abstract class BluetoothProfileConnector<T> {
            throw new SecurityException("Need BLUETOOTH permission");
        }

        if (mgr != null) {
        connect(context.getPackageName(), listener);
    }

    /** @hide */
    public void connect(String packageName, BluetoothProfile.ServiceListener listener) {
        mPackageName = packageName;
        mServiceListener = listener;

        try {
                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
            mBluetoothManager.registerStateChangeCallback(mBluetoothStateChangeCallback);
        } catch (RemoteException re) {
            logError("Failed to register state change callback. " + re);
        }
    }
    }

    void disconnect() {
    /** @hide */
    public void disconnect() {
        if (mServiceListener != null) {
            BluetoothProfile.ServiceListener listener = mServiceListener;
            mServiceListener = null;
            listener.onServiceDisconnected(mProfileId);
        }
        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
        if (mgr != null) {
        try {
                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
            mBluetoothManager.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
        } catch (RemoteException re) {
            logError("Failed to unregister state change callback" + re);
        }
    }
    }

    T getService() {
        return mService;
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.bluetooth;

import static com.google.common.truth.Truth.assertThat;

import android.content.ComponentName;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

/** Test cases for {@link BluetoothProfileConnector}. */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BluetoothProfileConnectorTest {
    static class FakeBluetoothManager extends IBluetoothManager.Default {
        private IBluetoothStateChangeCallback mStateChangeCallback;
        private IBluetoothProfileServiceConnection mServiceConnection;

        @Override
        public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
            mStateChangeCallback = callback;
        }

        @Override
        public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback)
                throws RemoteException {
            if (callback != mStateChangeCallback) throw new IllegalStateException();

            mStateChangeCallback.onBluetoothStateChange(false);

            mStateChangeCallback = null;
        }

        @Override
        public boolean bindBluetoothProfileService(
                int profile, String serviceName, IBluetoothProfileServiceConnection proxy) {
            mServiceConnection = proxy;
            return true;
        }

        @Override
        public void unbindBluetoothProfileService(
                int profile, IBluetoothProfileServiceConnection proxy) {
            if (proxy != mServiceConnection) throw new IllegalStateException();

            mServiceConnection = null;
        }
    }

    private BluetoothProfileConnector createBluetoothProfileConnector(
            IBluetoothManager bluetoothManager) {
        return new BluetoothProfileConnector(
                null, BluetoothProfile.HEADSET, "Headset", "HeadsetService", bluetoothManager) {
            public IBinder getServiceInterface(IBinder service) {
                return service;
            }
        };
    }

    @Test
    public void bind_registerServiceConnection() throws RemoteException {
        FakeBluetoothManager bluetoothManager = new FakeBluetoothManager();
        BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager);
        BluetoothProfile.ServiceListener listener = null;

        connector.connect("test.package", listener);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true);

        assertThat(bluetoothManager.mServiceConnection).isNotNull();
    }

    @Test
    public void unbind_unregisterServiceConnection() throws RemoteException {
        FakeBluetoothManager bluetoothManager = new FakeBluetoothManager();
        BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager);
        ComponentName componentName = new ComponentName("pkg", "cls");
        BluetoothProfile.ServiceListener listener = null;

        connector.connect("test.package", listener);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true);
        bluetoothManager.mServiceConnection.onServiceConnected(componentName, new Binder());
        bluetoothManager.mServiceConnection.onServiceDisconnected(componentName);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false);

        assertThat(bluetoothManager.mServiceConnection).isNull();
    }

    @Test
    public void upThenDown_unregisterServiceConnection() throws RemoteException {
        FakeBluetoothManager bluetoothManager = new FakeBluetoothManager();
        BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager);
        BluetoothProfile.ServiceListener listener = null;

        connector.connect("test.package", listener);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false);

        // TODO(b/302092694): Should be isNull
        assertThat(bluetoothManager.mServiceConnection).isNotNull();
    }

    @Test
    public void disconnectAfterConnect_unregisterCallbacks() {
        FakeBluetoothManager bluetoothManager = new FakeBluetoothManager();
        BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager);
        BluetoothProfile.ServiceListener listener = null;

        connector.connect("test.package", listener);
        connector.disconnect();

        assertThat(bluetoothManager.mServiceConnection).isNull();
        assertThat(bluetoothManager.mStateChangeCallback).isNull();
    }

    @Test
    public void disconnectAfterBind_unregisterCallbacks() throws RemoteException {
        FakeBluetoothManager bluetoothManager = new FakeBluetoothManager();
        BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager);
        BluetoothProfile.ServiceListener listener = null;

        connector.connect("test.package", listener);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true);
        connector.disconnect();

        // TODO(b/302092694): Should be isNull
        assertThat(bluetoothManager.mServiceConnection).isNotNull();
        assertThat(bluetoothManager.mStateChangeCallback).isNull();
    }

    @Test
    public void disconnectAfterUnbind_unregisterCallbacks() throws RemoteException {
        FakeBluetoothManager bluetoothManager = new FakeBluetoothManager();
        BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager);
        ComponentName componentName = new ComponentName("pkg", "cls");
        BluetoothProfile.ServiceListener listener = null;

        connector.connect("test.package", listener);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true);
        bluetoothManager.mServiceConnection.onServiceConnected(componentName, new Binder());
        bluetoothManager.mServiceConnection.onServiceDisconnected(componentName);
        bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false);
        connector.disconnect();

        assertThat(bluetoothManager.mServiceConnection).isNull();
        assertThat(bluetoothManager.mStateChangeCallback).isNull();
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -2037,7 +2037,9 @@ class BluetoothManagerService {
                    IBluetoothStateChangeCallback unregCallback =
                            (IBluetoothStateChangeCallback)msg.obj;
                    try {
                        // LINT.IfChange
                        unregCallback.onBluetoothStateChange(false);
                        // LINT.ThenChange(/framework/tests/unit/src/android/bluetooth/BluetoothProfileConnectorTest.java)
                    } catch (RemoteException e) {
                        Log.e(TAG, "UNREGISTER_STATE_CHANGE_CALLBACK: callback failed", e);
                    }