Loading framework/tests/bumble/bumble_config.json +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 } framework/tests/bumble/src/android/bluetooth/PandoraDevice.java +23 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } Loading @@ -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 Loading @@ -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); Loading framework/tests/bumble/src/android/bluetooth/SdpClientTest.java 0 → 100644 +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); } } framework/tests/bumble/src/android/bluetooth/Utils.java +12 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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)); } } Loading
framework/tests/bumble/bumble_config.json +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 }
framework/tests/bumble/src/android/bluetooth/PandoraDevice.java +23 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } Loading @@ -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 Loading @@ -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); Loading
framework/tests/bumble/src/android/bluetooth/SdpClientTest.java 0 → 100644 +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); } }
framework/tests/bumble/src/android/bluetooth/Utils.java +12 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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)); } }