1 /*
2  * Copyright (C) 2015 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.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
21 
22 import android.bluetooth.BluetoothA2dpSink;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothClass;
25 import android.bluetooth.BluetoothDevice;
26 import android.bluetooth.BluetoothProfile;
27 import android.bluetooth.BluetoothUuid;
28 import android.content.Context;
29 import android.os.ParcelUuid;
30 import android.util.Log;
31 
32 import com.android.settingslib.R;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 final class A2dpSinkProfile implements LocalBluetoothProfile {
38     private static final String TAG = "A2dpSinkProfile";
39 
40     private BluetoothA2dpSink mService;
41     private boolean mIsProfileReady;
42 
43     private final CachedBluetoothDeviceManager mDeviceManager;
44 
45     static final ParcelUuid[] SRC_UUIDS = {
46         BluetoothUuid.A2DP_SOURCE,
47         BluetoothUuid.ADV_AUDIO_DIST,
48     };
49 
50     static final String NAME = "A2DPSink";
51     private final LocalBluetoothProfileManager mProfileManager;
52 
53     // Order of this profile in device profiles list
54     private static final int ORDINAL = 5;
55 
56     // These callbacks run on the main thread.
57     private final class A2dpSinkServiceListener
58             implements BluetoothProfile.ServiceListener {
59 
onServiceConnected(int profile, BluetoothProfile proxy)60         public void onServiceConnected(int profile, BluetoothProfile proxy) {
61             mService = (BluetoothA2dpSink) proxy;
62             // We just bound to the service, so refresh the UI for any connected A2DP 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                     Log.w(TAG, "A2dpSinkProfile found new device: " + nextDevice);
70                     device = mDeviceManager.addDevice(nextDevice);
71                 }
72                 device.onProfileStateChanged(A2dpSinkProfile.this, BluetoothProfile.STATE_CONNECTED);
73                 device.refresh();
74             }
75             mIsProfileReady=true;
76         }
77 
onServiceDisconnected(int profile)78         public void onServiceDisconnected(int profile) {
79             mIsProfileReady=false;
80         }
81     }
82 
isProfileReady()83     public boolean isProfileReady() {
84         return mIsProfileReady;
85     }
86 
87     @Override
getProfileId()88     public int getProfileId() {
89         return BluetoothProfile.A2DP_SINK;
90     }
91 
A2dpSinkProfile(Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager)92     A2dpSinkProfile(Context context, CachedBluetoothDeviceManager deviceManager,
93             LocalBluetoothProfileManager profileManager) {
94         mDeviceManager = deviceManager;
95         mProfileManager = profileManager;
96         BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new A2dpSinkServiceListener(),
97                 BluetoothProfile.A2DP_SINK);
98     }
99 
accessProfileEnabled()100     public boolean accessProfileEnabled() {
101         return true;
102     }
103 
isAutoConnectable()104     public boolean isAutoConnectable() {
105         return true;
106     }
107 
getConnectedDevices()108     public List<BluetoothDevice> getConnectedDevices() {
109         if (mService == null) {
110             return new ArrayList<BluetoothDevice>(0);
111         }
112         return mService.getDevicesMatchingConnectionStates(
113               new int[] {BluetoothProfile.STATE_CONNECTED,
114                          BluetoothProfile.STATE_CONNECTING,
115                          BluetoothProfile.STATE_DISCONNECTING});
116     }
117 
getConnectionStatus(BluetoothDevice device)118     public int getConnectionStatus(BluetoothDevice device) {
119         if (mService == null) {
120             return BluetoothProfile.STATE_DISCONNECTED;
121         }
122         return mService.getConnectionState(device);
123     }
124 
125     @Override
isEnabled(BluetoothDevice device)126     public boolean isEnabled(BluetoothDevice device) {
127         if (mService == null) {
128             return false;
129         }
130         return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
131     }
132 
133     @Override
getConnectionPolicy(BluetoothDevice device)134     public int getConnectionPolicy(BluetoothDevice device) {
135         if (mService == null) {
136             return CONNECTION_POLICY_FORBIDDEN;
137         }
138         return mService.getConnectionPolicy(device);
139     }
140 
141     @Override
setEnabled(BluetoothDevice device, boolean enabled)142     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
143         boolean isEnabled = false;
144         if (mService == null) {
145             return false;
146         }
147         if (enabled) {
148             if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
149                 isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
150             }
151         } else {
152             isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
153         }
154 
155         return isEnabled;
156     }
157 
isAudioPlaying()158     boolean isAudioPlaying() {
159         if (mService == null) {
160             return false;
161         }
162         List<BluetoothDevice> srcs = mService.getConnectedDevices();
163         if (!srcs.isEmpty()) {
164             if (mService.isAudioPlaying(srcs.get(0))) {
165                 return true;
166             }
167         }
168         return false;
169     }
170 
toString()171     public String toString() {
172         return NAME;
173     }
174 
getOrdinal()175     public int getOrdinal() {
176         return ORDINAL;
177     }
178 
getNameResource(BluetoothDevice device)179     public int getNameResource(BluetoothDevice device) {
180         // we need to have same string in UI for even SINK Media Audio.
181         return R.string.bluetooth_profile_a2dp;
182     }
183 
getSummaryResourceForDevice(BluetoothDevice device)184     public int getSummaryResourceForDevice(BluetoothDevice device) {
185         int state = getConnectionStatus(device);
186         switch (state) {
187             case BluetoothProfile.STATE_DISCONNECTED:
188                 return R.string.bluetooth_a2dp_profile_summary_use_for;
189 
190             case BluetoothProfile.STATE_CONNECTED:
191                 return R.string.bluetooth_a2dp_profile_summary_connected;
192 
193             default:
194                 return BluetoothUtils.getConnectionStateSummary(state);
195         }
196     }
197 
getDrawableResource(BluetoothClass btClass)198     public int getDrawableResource(BluetoothClass btClass) {
199         return com.android.internal.R.drawable.ic_bt_headphones_a2dp;
200     }
201 
finalize()202     protected void finalize() {
203         Log.d(TAG, "finalize()");
204         if (mService != null) {
205             try {
206                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP_SINK,
207                                                                        mService);
208                 mService = null;
209             }catch (Throwable t) {
210                 Log.w(TAG, "Error cleaning up A2DP proxy", t);
211             }
212         }
213     }
214 }
215