1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.media; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.bluetooth.BluetoothA2dp; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothHearingAid; 25 import android.bluetooth.BluetoothLeAudio; 26 import android.bluetooth.BluetoothProfile; 27 import android.content.Context; 28 29 import java.util.Objects; 30 31 /* package */ class BluetoothProfileMonitor { 32 33 /* package */ static final long GROUP_ID_NO_GROUP = -1L; 34 35 @NonNull 36 private final ProfileListener mProfileListener = new ProfileListener(); 37 38 @NonNull 39 private final Context mContext; 40 @NonNull 41 private final BluetoothAdapter mBluetoothAdapter; 42 43 @Nullable 44 private BluetoothA2dp mA2dpProfile; 45 @Nullable 46 private BluetoothHearingAid mHearingAidProfile; 47 @Nullable 48 private BluetoothLeAudio mLeAudioProfile; 49 50 @Nullable 51 private OnProfileChangedListener mOnProfileChangedListener; 52 BluetoothProfileMonitor(@onNull Context context, @NonNull BluetoothAdapter bluetoothAdapter)53 BluetoothProfileMonitor(@NonNull Context context, 54 @NonNull BluetoothAdapter bluetoothAdapter) { 55 Objects.requireNonNull(context); 56 Objects.requireNonNull(bluetoothAdapter); 57 58 mContext = context; 59 mBluetoothAdapter = bluetoothAdapter; 60 } 61 setOnProfileChangedListener( @onNull OnProfileChangedListener listener)62 /* package */ synchronized void setOnProfileChangedListener( 63 @NonNull OnProfileChangedListener listener) { 64 mOnProfileChangedListener = listener; 65 } 66 start()67 /* package */ void start() { 68 mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); 69 mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); 70 mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO); 71 } 72 isProfileSupported(int profile, @NonNull BluetoothDevice device)73 /* package */ boolean isProfileSupported(int profile, @NonNull BluetoothDevice device) { 74 BluetoothProfile bluetoothProfile; 75 76 synchronized (this) { 77 switch (profile) { 78 case BluetoothProfile.A2DP: 79 bluetoothProfile = mA2dpProfile; 80 break; 81 case BluetoothProfile.LE_AUDIO: 82 bluetoothProfile = mLeAudioProfile; 83 break; 84 case BluetoothProfile.HEARING_AID: 85 bluetoothProfile = mHearingAidProfile; 86 break; 87 default: 88 throw new IllegalArgumentException(profile 89 + " is not supported as Bluetooth profile"); 90 } 91 } 92 93 if (bluetoothProfile == null) { 94 return false; 95 } 96 97 return bluetoothProfile.getConnectedDevices().contains(device); 98 } 99 getGroupId(int profile, @NonNull BluetoothDevice device)100 /* package */ long getGroupId(int profile, @NonNull BluetoothDevice device) { 101 synchronized (this) { 102 switch (profile) { 103 case BluetoothProfile.A2DP: 104 return GROUP_ID_NO_GROUP; 105 case BluetoothProfile.LE_AUDIO: 106 return mLeAudioProfile == null ? GROUP_ID_NO_GROUP : mLeAudioProfile.getGroupId( 107 device); 108 case BluetoothProfile.HEARING_AID: 109 return mHearingAidProfile == null 110 ? GROUP_ID_NO_GROUP : mHearingAidProfile.getHiSyncId(device); 111 default: 112 throw new IllegalArgumentException(profile 113 + " is not supported as Bluetooth profile"); 114 } 115 } 116 } 117 118 /* package */ interface OnProfileChangedListener { onProfileChange(int profile)119 void onProfileChange(int profile); 120 } 121 122 private final class ProfileListener implements BluetoothProfile.ServiceListener { 123 @Override onServiceConnected(int profile, BluetoothProfile proxy)124 public void onServiceConnected(int profile, BluetoothProfile proxy) { 125 OnProfileChangedListener listener; 126 127 synchronized (BluetoothProfileMonitor.this) { 128 switch (profile) { 129 case BluetoothProfile.A2DP: 130 mA2dpProfile = (BluetoothA2dp) proxy; 131 break; 132 case BluetoothProfile.HEARING_AID: 133 mHearingAidProfile = (BluetoothHearingAid) proxy; 134 break; 135 case BluetoothProfile.LE_AUDIO: 136 mLeAudioProfile = (BluetoothLeAudio) proxy; 137 break; 138 default: 139 return; 140 } 141 142 listener = mOnProfileChangedListener; 143 } 144 145 if (listener != null) { 146 listener.onProfileChange(profile); 147 } 148 } 149 150 @Override onServiceDisconnected(int profile)151 public void onServiceDisconnected(int profile) { 152 OnProfileChangedListener listener; 153 154 synchronized (BluetoothProfileMonitor.this) { 155 switch (profile) { 156 case BluetoothProfile.A2DP: 157 mA2dpProfile = null; 158 break; 159 case BluetoothProfile.HEARING_AID: 160 mHearingAidProfile = null; 161 break; 162 case BluetoothProfile.LE_AUDIO: 163 mLeAudioProfile = null; 164 break; 165 default: 166 return; 167 } 168 169 listener = mOnProfileChangedListener; 170 } 171 172 if (listener != null) { 173 listener.onProfileChange(profile); 174 } 175 } 176 } 177 178 } 179