1 package com.google.android.mobly.snippet.bundled.bluetooth.profiles; 2 3 import android.annotation.TargetApi; 4 import android.bluetooth.BluetoothAdapter; 5 import android.bluetooth.BluetoothDevice; 6 import android.bluetooth.BluetoothHearingAid; 7 import android.bluetooth.BluetoothProfile; 8 import android.content.Context; 9 import android.content.IntentFilter; 10 import android.os.Build; 11 import android.os.Bundle; 12 import androidx.test.platform.app.InstrumentationRegistry; 13 import com.google.android.mobly.snippet.Snippet; 14 import com.google.android.mobly.snippet.bundled.bluetooth.BluetoothAdapterSnippet; 15 import com.google.android.mobly.snippet.bundled.bluetooth.PairingBroadcastReceiver; 16 import com.google.android.mobly.snippet.bundled.utils.JsonSerializer; 17 import com.google.android.mobly.snippet.bundled.utils.Utils; 18 import com.google.android.mobly.snippet.rpc.Rpc; 19 import com.google.android.mobly.snippet.rpc.RpcMinSdk; 20 import com.google.common.base.Ascii; 21 import java.util.ArrayList; 22 23 public class BluetoothHearingAidSnippet implements Snippet { 24 private static class BluetoothHearingAidSnippetException extends Exception { 25 private static final long serialVersionUID = 1; 26 BluetoothHearingAidSnippetException(String msg)27 BluetoothHearingAidSnippetException(String msg) { 28 super(msg); 29 } 30 } 31 32 private static final int TIMEOUT_SEC = 60; 33 34 private final Context context; 35 private static boolean isHearingAidProfileReady = false; 36 private static BluetoothHearingAid hearingAidProfile; 37 private final JsonSerializer jsonSerializer = new JsonSerializer(); 38 39 @TargetApi(Build.VERSION_CODES.Q) BluetoothHearingAidSnippet()40 public BluetoothHearingAidSnippet() { 41 context = InstrumentationRegistry.getInstrumentation().getContext(); 42 BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 43 bluetoothAdapter.getProfileProxy( 44 context, new HearingAidServiceListener(), BluetoothProfile.HEARING_AID); 45 Utils.waitUntil(() -> isHearingAidProfileReady, TIMEOUT_SEC); 46 } 47 48 @TargetApi(Build.VERSION_CODES.Q) 49 private static class HearingAidServiceListener implements BluetoothProfile.ServiceListener { 50 @Override onServiceConnected(int var1, BluetoothProfile profile)51 public void onServiceConnected(int var1, BluetoothProfile profile) { 52 hearingAidProfile = (BluetoothHearingAid) profile; 53 isHearingAidProfileReady = true; 54 } 55 56 @Override onServiceDisconnected(int var1)57 public void onServiceDisconnected(int var1) { 58 isHearingAidProfileReady = false; 59 } 60 } 61 62 @TargetApi(Build.VERSION_CODES.Q) 63 @RpcMinSdk(Build.VERSION_CODES.Q) 64 @Rpc(description = "Connects to a paired or discovered device with HA profile.") btHearingAidConnect(String deviceAddress)65 public void btHearingAidConnect(String deviceAddress) throws Throwable { 66 BluetoothDevice device = BluetoothAdapterSnippet.getKnownDeviceByAddress(deviceAddress); 67 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST); 68 context.registerReceiver(new PairingBroadcastReceiver(context), filter); 69 Utils.invokeByReflection(hearingAidProfile, "connect", device); 70 if (!Utils.waitUntil( 71 () -> 72 hearingAidProfile.getConnectionState(device) 73 == BluetoothHearingAid.STATE_CONNECTED, 74 TIMEOUT_SEC)) { 75 throw new BluetoothHearingAidSnippetException( 76 String.format( 77 "Failed to connect to device %s|%s with HA profile within %d sec.", 78 device.getName(), device.getAddress(), TIMEOUT_SEC)); 79 } 80 } 81 82 @Rpc(description = "Disconnects a device from HA profile.") btHearingAidDisconnect(String deviceAddress)83 public void btHearingAidDisconnect(String deviceAddress) throws Throwable { 84 BluetoothDevice device = getConnectedBluetoothDevice(deviceAddress); 85 Utils.invokeByReflection(hearingAidProfile, "disconnect", device); 86 if (!Utils.waitUntil( 87 () -> 88 hearingAidProfile.getConnectionState(device) 89 == BluetoothHearingAid.STATE_DISCONNECTED, 90 TIMEOUT_SEC)) { 91 throw new BluetoothHearingAidSnippetException( 92 String.format( 93 "Failed to disconnect to device %s|%s with HA profile within %d sec.", 94 device.getName(), device.getAddress(), TIMEOUT_SEC)); 95 } 96 } 97 98 @Rpc(description = "Gets all the devices currently connected via HA profile.") btHearingAidGetConnectedDevices()99 public ArrayList<Bundle> btHearingAidGetConnectedDevices() { 100 return jsonSerializer.serializeBluetoothDeviceList(hearingAidProfile.getConnectedDevices()); 101 } 102 getConnectedBluetoothDevice(String deviceAddress)103 private static BluetoothDevice getConnectedBluetoothDevice(String deviceAddress) 104 throws BluetoothHearingAidSnippetException { 105 for (BluetoothDevice device : hearingAidProfile.getConnectedDevices()) { 106 if (Ascii.equalsIgnoreCase(device.getAddress(), deviceAddress)) { 107 return device; 108 } 109 } 110 throw new BluetoothHearingAidSnippetException(String.format( 111 "No device with address %s is connected via HA Profile.", deviceAddress)); 112 } 113 114 @Override shutdown()115 public void shutdown() {} 116 } 117