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 
BluetoothProfileMonitor(@onNull Context context, @NonNull BluetoothAdapter bluetoothAdapter)50     BluetoothProfileMonitor(@NonNull Context context,
51             @NonNull BluetoothAdapter bluetoothAdapter) {
52         mContext = Objects.requireNonNull(context);
53         mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
54     }
55 
start()56     /* package */ void start() {
57         mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
58         mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
59         mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
60     }
61 
isProfileSupported(int profile, @NonNull BluetoothDevice device)62     /* package */ boolean isProfileSupported(int profile, @NonNull BluetoothDevice device) {
63         BluetoothProfile bluetoothProfile;
64 
65         synchronized (this) {
66             switch (profile) {
67                 case BluetoothProfile.A2DP:
68                     bluetoothProfile = mA2dpProfile;
69                     break;
70                 case BluetoothProfile.LE_AUDIO:
71                     bluetoothProfile = mLeAudioProfile;
72                     break;
73                 case BluetoothProfile.HEARING_AID:
74                     bluetoothProfile = mHearingAidProfile;
75                     break;
76                 default:
77                     throw new IllegalArgumentException(profile
78                             + " is not supported as Bluetooth profile");
79             }
80         }
81 
82         if (bluetoothProfile == null) {
83             return false;
84         }
85 
86         return bluetoothProfile.getConnectedDevices().contains(device);
87     }
88 
getGroupId(int profile, @NonNull BluetoothDevice device)89     /* package */ long getGroupId(int profile, @NonNull BluetoothDevice device) {
90         synchronized (this) {
91             switch (profile) {
92                 case BluetoothProfile.A2DP:
93                     return GROUP_ID_NO_GROUP;
94                 case BluetoothProfile.LE_AUDIO:
95                     return mLeAudioProfile == null ? GROUP_ID_NO_GROUP : mLeAudioProfile.getGroupId(
96                             device);
97                 case BluetoothProfile.HEARING_AID:
98                     return mHearingAidProfile == null
99                             ? GROUP_ID_NO_GROUP : mHearingAidProfile.getHiSyncId(device);
100                 default:
101                     throw new IllegalArgumentException(profile
102                             + " is not supported as Bluetooth profile");
103             }
104         }
105     }
106 
107     private final class ProfileListener implements BluetoothProfile.ServiceListener {
108         @Override
onServiceConnected(int profile, BluetoothProfile proxy)109         public void onServiceConnected(int profile, BluetoothProfile proxy) {
110             synchronized (BluetoothProfileMonitor.this) {
111                 switch (profile) {
112                     case BluetoothProfile.A2DP:
113                         mA2dpProfile = (BluetoothA2dp) proxy;
114                         break;
115                     case BluetoothProfile.HEARING_AID:
116                         mHearingAidProfile = (BluetoothHearingAid) proxy;
117                         break;
118                     case BluetoothProfile.LE_AUDIO:
119                         mLeAudioProfile = (BluetoothLeAudio) proxy;
120                         break;
121                 }
122             }
123         }
124 
125         @Override
onServiceDisconnected(int profile)126         public void onServiceDisconnected(int profile) {
127             synchronized (BluetoothProfileMonitor.this) {
128                 switch (profile) {
129                     case BluetoothProfile.A2DP:
130                         mA2dpProfile = null;
131                         break;
132                     case BluetoothProfile.HEARING_AID:
133                         mHearingAidProfile = null;
134                         break;
135                     case BluetoothProfile.LE_AUDIO:
136                         mLeAudioProfile = null;
137                         break;
138                 }
139             }
140         }
141     }
142 }
143