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

Commit 7affa558 authored by Chris Manton's avatar Chris Manton
Browse files

Validate service discovery procedure

Bug: 265466975
Test: atest -v SdpClientTest

Change-Id: I2e2bd9d1b2a7d615d7d0c84cc36728147c766898
parent 0da4cf20
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
{
    "advertising_interval": 200,
  "address": "51:F7:A8:75:AC:5E"
    "address": "51:F7:A8:75:AC:5E",
    "classic_enabled": true
}
+23 −12
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package android.bluetooth;

import android.util.Log;

import androidx.test.core.app.ApplicationProvider;

import com.google.protobuf.Empty;

import io.grpc.ManagedChannel;
@@ -29,17 +31,17 @@ import java.util.concurrent.TimeUnit;

import pandora.DckGrpc;
import pandora.HostGrpc;
import pandora.HostProto;

public final class PandoraDevice extends ExternalResource {
    private static final String TAG = PandoraDevice.class.getSimpleName();

    private final String mAddress;
    private final String mNetworkAddress;
    private String mPublicBluetoothAddress;
    private final int mPort;

    private ManagedChannel mChannel;

    public PandoraDevice(String address, int port) {
        mAddress = address;
    public PandoraDevice(String networkAddress, int port) {
        mNetworkAddress = networkAddress;
        mPort = port;
    }

@@ -53,22 +55,21 @@ public final class PandoraDevice extends ExternalResource {
        // FactoryReset is killing the server and restarting all channels created before the server
        // restarted that cannot be reused
        ManagedChannel channel =
                OkHttpChannelBuilder.forAddress(mAddress, mPort).usePlaintext().build();

                OkHttpChannelBuilder.forAddress(mNetworkAddress, mPort).usePlaintext().build();
        HostGrpc.HostBlockingStub stub = HostGrpc.newBlockingStub(channel);
        stub.factoryReset(Empty.getDefaultInstance());

        try {
            // terminate the channel
            channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        mChannel = OkHttpChannelBuilder.forAddress(mAddress, mPort).usePlaintext().build();
        mChannel = OkHttpChannelBuilder.forAddress(mNetworkAddress, mPort).usePlaintext().build();
        stub = HostGrpc.newBlockingStub(mChannel);

        HostProto.ReadLocalAddressResponse readLocalAddressResponse =
                stub.withWaitForReady().readLocalAddress(Empty.getDefaultInstance());
        mPublicBluetoothAddress =
                Utils.addressStringFromByteString(readLocalAddressResponse.getAddress());
    }

    @Override
@@ -83,6 +84,16 @@ public final class PandoraDevice extends ExternalResource {
        }
    }

    /**
     * @return bumble as a remote device
     */
    public BluetoothDevice getRemoteDevice() {
        return ApplicationProvider.getApplicationContext()
                .getSystemService(BluetoothManager.class)
                .getAdapter()
                .getRemoteDevice(mPublicBluetoothAddress);
    }

    /** Get Pandora Host service */
    public HostGrpc.HostStub host() {
        return HostGrpc.newStub(mChannel);
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.os.Parcelable;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;

import com.android.compatibility.common.util.AdoptShellPermissionsRule;

import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.ByteString;

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

import java.util.ArrayList;
import java.util.UUID;

import pandora.HostProto.ConnectRequest;
import pandora.HostProto.WaitConnectionRequest;

/** Test cases for {@link ServiceDiscoveryManager}. */
@RunWith(AndroidJUnit4.class)
public class SdpClientTest {
    private static final String TAG = "SdpClientTest";

    private final Context mContext = ApplicationProvider.getApplicationContext();
    private final BluetoothManager mManager = mContext.getSystemService(BluetoothManager.class);
    private final BluetoothAdapter mAdapter = mManager.getAdapter();

    private SettableFuture<ArrayList<UUID>> mFutureIntent;

    @Rule public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule();

    @Rule public final PandoraDevice mBumble = new PandoraDevice();

    private BroadcastReceiver mConnectionStateReceiver =
            new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    if (BluetoothDevice.ACTION_UUID.equals(intent.getAction())) {
                        Parcelable[] parcelable =
                                (Parcelable[]) intent.getExtra(BluetoothDevice.EXTRA_UUID);
                        if (parcelable != null) {
                            ArrayList<UUID> list = new ArrayList<UUID>();
                            for (Parcelable p : parcelable) {
                                ParcelUuid uuid = (ParcelUuid) p;
                                list.add(uuid.getUuid());
                            }
                            mFutureIntent.set(list);
                        }
                    }
                }
            };

    @Test
    public void remoteConnectServiceDiscoveryTest() throws Exception {
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_UUID);
        mContext.registerReceiver(mConnectionStateReceiver, filter);

        mFutureIntent = SettableFuture.create();

        String local_addr = mAdapter.getAddress();
        byte[] local_bytes_addr = Utils.addressBytesFromString(local_addr);

        // Initiate connect from remote
        mBumble.hostBlocking()
                .connect(
                        ConnectRequest.newBuilder()
                                .setAddress(ByteString.copyFrom(local_bytes_addr))
                                .build());

        // Wait until connection is stable
        mBumble.hostBlocking()
                .waitConnection(
                        WaitConnectionRequest.newBuilder()
                                .setAddress(ByteString.copyFrom(local_bytes_addr))
                                .build());

        // Get the remote device
        BluetoothDevice device = mBumble.getRemoteDevice();

        // Execute service discovery procedure
        assertThat(device.fetchUuidsWithSdp()).isTrue();

        ArrayList<UUID> list = mFutureIntent.get();
        assertThat(list.isEmpty()).isFalse();

        mContext.unregisterReceiver(mConnectionStateReceiver);
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package android.bluetooth;

import static com.google.common.io.BaseEncoding.base16;

import com.google.protobuf.ByteString;

import java.util.Locale;

public final class Utils {
    public static final String BUMBLE_RANDOM_ADDRESS = "51:F7:A8:75:AC:5E";

@@ -31,4 +35,12 @@ public final class Utils {
        }
        return refAddrBuilder.toString();
    }

    /**
     * @param address String representing Bluetooth address (case insensitive).
     * @return Decoded address.
     */
    public static byte[] addressBytesFromString(String address) {
        return base16().upperCase().withSeparator(":", 2).decode(address.toUpperCase(Locale.US));
    }
}