1 /*
2  * Copyright (C) 2018 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.settingslib.bluetooth;
18 
19 import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
20 import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL;
21 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
22 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
23 
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothClass;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothHearingAid;
28 import android.bluetooth.BluetoothProfile;
29 import android.content.Context;
30 import android.util.Log;
31 
32 import com.android.settingslib.R;
33 import com.android.settingslib.Utils;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 public class HearingAidProfile implements LocalBluetoothProfile {
39     private static final String TAG = "HearingAidProfile";
40     private static boolean V = true;
41 
42     private Context mContext;
43 
44     private BluetoothHearingAid mService;
45     private boolean mIsProfileReady;
46 
47     private final CachedBluetoothDeviceManager mDeviceManager;
48 
49     static final String NAME = "HearingAid";
50     private final LocalBluetoothProfileManager mProfileManager;
51     private final BluetoothAdapter mBluetoothAdapter;
52 
53     // Order of this profile in device profiles list
54     private static final int ORDINAL = 1;
55 
56     // These callbacks run on the main thread.
57     private final class HearingAidServiceListener
58             implements BluetoothProfile.ServiceListener {
59 
onServiceConnected(int profile, BluetoothProfile proxy)60         public void onServiceConnected(int profile, BluetoothProfile proxy) {
61             mService = (BluetoothHearingAid) proxy;
62             // We just bound to the service, so refresh the UI for any connected HearingAid devices.
63             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
64             while (!deviceList.isEmpty()) {
65                 BluetoothDevice nextDevice = deviceList.remove(0);
66                 CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
67                 // we may add a new device here, but generally this should not happen
68                 if (device == null) {
69                     if (V) {
70                         Log.d(TAG, "HearingAidProfile found new device: " + nextDevice);
71                     }
72                     device = mDeviceManager.addDevice(nextDevice);
73                 }
74                 device.onProfileStateChanged(HearingAidProfile.this,
75                         BluetoothProfile.STATE_CONNECTED);
76                 device.refresh();
77             }
78 
79             // Check current list of CachedDevices to see if any are Hearing Aid devices.
80             mDeviceManager.updateHearingAidsDevices();
81             mIsProfileReady=true;
82             mProfileManager.callServiceConnectedListeners();
83         }
84 
onServiceDisconnected(int profile)85         public void onServiceDisconnected(int profile) {
86             mIsProfileReady=false;
87         }
88     }
89 
isProfileReady()90     public boolean isProfileReady() {
91         return mIsProfileReady;
92     }
93 
94     @Override
getProfileId()95     public int getProfileId() {
96         return BluetoothProfile.HEARING_AID;
97     }
98 
HearingAidProfile(Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager)99     HearingAidProfile(Context context, CachedBluetoothDeviceManager deviceManager,
100             LocalBluetoothProfileManager profileManager) {
101         mContext = context;
102         mDeviceManager = deviceManager;
103         mProfileManager = profileManager;
104         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
105         mBluetoothAdapter.getProfileProxy(context,
106                 new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
107     }
108 
accessProfileEnabled()109     public boolean accessProfileEnabled() {
110         return false;
111     }
112 
isAutoConnectable()113     public boolean isAutoConnectable() {
114         return true;
115     }
116 
117     /**
118      * Get Hearing Aid devices matching connection states{
119      * @code BluetoothProfile.STATE_CONNECTED,
120      * @code BluetoothProfile.STATE_CONNECTING,
121      * @code BluetoothProfile.STATE_DISCONNECTING}
122      *
123      * @return Matching device list
124      */
getConnectedDevices()125     public List<BluetoothDevice> getConnectedDevices() {
126         return getDevicesByStates(new int[] {
127                 BluetoothProfile.STATE_CONNECTED,
128                 BluetoothProfile.STATE_CONNECTING,
129                 BluetoothProfile.STATE_DISCONNECTING});
130     }
131 
132     /**
133      * Get Hearing Aid devices matching connection states{
134      * @code BluetoothProfile.STATE_DISCONNECTED,
135      * @code BluetoothProfile.STATE_CONNECTED,
136      * @code BluetoothProfile.STATE_CONNECTING,
137      * @code BluetoothProfile.STATE_DISCONNECTING}
138      *
139      * @return Matching device list
140      */
getConnectableDevices()141     public List<BluetoothDevice> getConnectableDevices() {
142         return getDevicesByStates(new int[] {
143                 BluetoothProfile.STATE_DISCONNECTED,
144                 BluetoothProfile.STATE_CONNECTED,
145                 BluetoothProfile.STATE_CONNECTING,
146                 BluetoothProfile.STATE_DISCONNECTING});
147     }
148 
getDevicesByStates(int[] states)149     private List<BluetoothDevice> getDevicesByStates(int[] states) {
150         if (mService == null) {
151             return new ArrayList<BluetoothDevice>(0);
152         }
153         return mService.getDevicesMatchingConnectionStates(states);
154     }
155 
getConnectionStatus(BluetoothDevice device)156     public int getConnectionStatus(BluetoothDevice device) {
157         if (mService == null) {
158             return BluetoothProfile.STATE_DISCONNECTED;
159         }
160         return mService.getConnectionState(device);
161     }
162 
setActiveDevice(BluetoothDevice device)163     public boolean setActiveDevice(BluetoothDevice device) {
164         if (mBluetoothAdapter == null) {
165             return false;
166         }
167         int profiles = Utils.isAudioModeOngoingCall(mContext)
168                 ? ACTIVE_DEVICE_PHONE_CALL
169                 : ACTIVE_DEVICE_AUDIO;
170         return device == null
171                 ? mBluetoothAdapter.removeActiveDevice(profiles)
172                 : mBluetoothAdapter.setActiveDevice(device, profiles);
173     }
174 
getActiveDevices()175     public List<BluetoothDevice> getActiveDevices() {
176         if (mService == null) return new ArrayList<>();
177         return mService.getActiveDevices();
178     }
179 
180     @Override
isEnabled(BluetoothDevice device)181     public boolean isEnabled(BluetoothDevice device) {
182         if (mService == null || device == null) {
183             return false;
184         }
185         return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
186     }
187 
188     @Override
getConnectionPolicy(BluetoothDevice device)189     public int getConnectionPolicy(BluetoothDevice device) {
190         if (mService == null || device == null) {
191             return CONNECTION_POLICY_FORBIDDEN;
192         }
193         return mService.getConnectionPolicy(device);
194     }
195 
196     @Override
setEnabled(BluetoothDevice device, boolean enabled)197     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
198         boolean isEnabled = false;
199         if (mService == null || device == null) {
200             return false;
201         }
202         if (enabled) {
203             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
204                 isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
205             }
206         } else {
207             isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
208         }
209 
210         return isEnabled;
211     }
212 
setVolume(int volume)213     public void setVolume(int volume) {
214         if (mService == null) {
215             return;
216         }
217         mService.setVolume(volume);
218     }
219 
getHiSyncId(BluetoothDevice device)220     public long getHiSyncId(BluetoothDevice device) {
221         if (mService == null || device == null) {
222             return BluetoothHearingAid.HI_SYNC_ID_INVALID;
223         }
224         return mService.getHiSyncId(device);
225     }
226 
toString()227     public String toString() {
228         return NAME;
229     }
230 
getOrdinal()231     public int getOrdinal() {
232         return ORDINAL;
233     }
234 
getNameResource(BluetoothDevice device)235     public int getNameResource(BluetoothDevice device) {
236         return R.string.bluetooth_profile_hearing_aid;
237     }
238 
getSummaryResourceForDevice(BluetoothDevice device)239     public int getSummaryResourceForDevice(BluetoothDevice device) {
240         int state = getConnectionStatus(device);
241         switch (state) {
242             case BluetoothProfile.STATE_DISCONNECTED:
243                 return R.string.bluetooth_hearing_aid_profile_summary_use_for;
244 
245             case BluetoothProfile.STATE_CONNECTED:
246                 return R.string.bluetooth_hearing_aid_profile_summary_connected;
247 
248             default:
249                 return BluetoothUtils.getConnectionStateSummary(state);
250         }
251     }
252 
getDrawableResource(BluetoothClass btClass)253     public int getDrawableResource(BluetoothClass btClass) {
254         return com.android.internal.R.drawable.ic_bt_hearing_aid;
255     }
256 
finalize()257     protected void finalize() {
258         Log.d(TAG, "finalize()");
259         if (mService != null) {
260             try {
261                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
262                                                                        mService);
263                 mService = null;
264             }catch (Throwable t) {
265                 Log.w(TAG, "Error cleaning up Hearing Aid proxy", t);
266             }
267         }
268     }
269 }
270