1 /*
2  * Copyright (C) 2011 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 android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothProfile;
22 import android.bluetooth.le.BluetoothLeScanner;
23 import android.content.Context;
24 import android.os.ParcelUuid;
25 import android.util.Log;
26 
27 import java.util.List;
28 import java.util.Set;
29 
30 /**
31  * LocalBluetoothAdapter provides an interface between the Settings app
32  * and the functionality of the local {@link BluetoothAdapter}, specifically
33  * those related to state transitions of the adapter itself.
34  *
35  * <p>Connection and bonding state changes affecting specific devices
36  * are handled by {@link CachedBluetoothDeviceManager},
37  * {@link BluetoothEventManager}, and {@link LocalBluetoothProfileManager}.
38  */
39 public class LocalBluetoothAdapter {
40     private static final String TAG = "LocalBluetoothAdapter";
41 
42     /** This class does not allow direct access to the BluetoothAdapter. */
43     private final BluetoothAdapter mAdapter;
44 
45     private LocalBluetoothProfileManager mProfileManager;
46 
47     private static LocalBluetoothAdapter sInstance;
48 
49     private int mState = BluetoothAdapter.ERROR;
50 
51     private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
52 
53     private long mLastScan;
54 
LocalBluetoothAdapter(BluetoothAdapter adapter)55     private LocalBluetoothAdapter(BluetoothAdapter adapter) {
56         mAdapter = adapter;
57     }
58 
setProfileManager(LocalBluetoothProfileManager manager)59     void setProfileManager(LocalBluetoothProfileManager manager) {
60         mProfileManager = manager;
61     }
62 
63     /**
64      * Get the singleton instance of the LocalBluetoothAdapter. If this device
65      * doesn't support Bluetooth, then null will be returned. Callers must be
66      * prepared to handle a null return value.
67      * @return the LocalBluetoothAdapter object, or null if not supported
68      */
getInstance()69     static synchronized LocalBluetoothAdapter getInstance() {
70         if (sInstance == null) {
71             BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
72             if (adapter != null) {
73                 sInstance = new LocalBluetoothAdapter(adapter);
74             }
75         }
76 
77         return sInstance;
78     }
79 
80     // Pass-through BluetoothAdapter methods that we can intercept if necessary
81 
cancelDiscovery()82     public void cancelDiscovery() {
83         mAdapter.cancelDiscovery();
84     }
85 
enable()86     public boolean enable() {
87         return mAdapter.enable();
88     }
89 
disable()90     public boolean disable() {
91         return mAdapter.disable();
92     }
93 
getAddress()94     public String getAddress() {
95         return mAdapter.getAddress();
96     }
97 
getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile)98     void getProfileProxy(Context context,
99             BluetoothProfile.ServiceListener listener, int profile) {
100         mAdapter.getProfileProxy(context, listener, profile);
101     }
102 
getBondedDevices()103     public Set<BluetoothDevice> getBondedDevices() {
104         return mAdapter.getBondedDevices();
105     }
106 
getName()107     public String getName() {
108         return mAdapter.getName();
109     }
110 
getScanMode()111     public int getScanMode() {
112         return mAdapter.getScanMode();
113     }
114 
getBluetoothLeScanner()115     public BluetoothLeScanner getBluetoothLeScanner() {
116         return mAdapter.getBluetoothLeScanner();
117     }
118 
getState()119     public int getState() {
120         return mAdapter.getState();
121     }
122 
getUuids()123     public ParcelUuid[] getUuids() {
124         return mAdapter.getUuids();
125     }
126 
isDiscovering()127     public boolean isDiscovering() {
128         return mAdapter.isDiscovering();
129     }
130 
isEnabled()131     public boolean isEnabled() {
132         return mAdapter.isEnabled();
133     }
134 
getConnectionState()135     public int getConnectionState() {
136         return mAdapter.getConnectionState();
137     }
138 
setDiscoverableTimeout(int timeout)139     public void setDiscoverableTimeout(int timeout) {
140         mAdapter.setDiscoverableTimeout(timeout);
141     }
142 
getDiscoveryEndMillis()143     public long getDiscoveryEndMillis() {
144         return mAdapter.getDiscoveryEndMillis();
145     }
146 
setName(String name)147     public void setName(String name) {
148         mAdapter.setName(name);
149     }
150 
setScanMode(int mode)151     public void setScanMode(int mode) {
152         mAdapter.setScanMode(mode);
153     }
154 
setScanMode(int mode, int duration)155     public boolean setScanMode(int mode, int duration) {
156         return mAdapter.setScanMode(mode, duration);
157     }
158 
startScanning(boolean force)159     public void startScanning(boolean force) {
160         // Only start if we're not already scanning
161         if (!mAdapter.isDiscovering()) {
162             if (!force) {
163                 // Don't scan more than frequently than SCAN_EXPIRATION_MS,
164                 // unless forced
165                 if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
166                     return;
167                 }
168 
169                 // If we are playing music, don't scan unless forced.
170                 A2dpProfile a2dp = mProfileManager.getA2dpProfile();
171                 if (a2dp != null && a2dp.isA2dpPlaying()) {
172                     return;
173                 }
174                 A2dpSinkProfile a2dpSink = mProfileManager.getA2dpSinkProfile();
175                 if ((a2dpSink != null) && (a2dpSink.isA2dpPlaying())){
176                     return;
177                 }
178             }
179 
180             if (mAdapter.startDiscovery()) {
181                 mLastScan = System.currentTimeMillis();
182             }
183         }
184     }
185 
stopScanning()186     public void stopScanning() {
187         if (mAdapter.isDiscovering()) {
188             mAdapter.cancelDiscovery();
189         }
190     }
191 
getBluetoothState()192     public synchronized int getBluetoothState() {
193         // Always sync state, in case it changed while paused
194         syncBluetoothState();
195         return mState;
196     }
197 
setBluetoothStateInt(int state)198     void setBluetoothStateInt(int state) {
199         synchronized(this) {
200             if (mState == state) {
201                 return;
202             }
203             mState = state;
204         }
205 
206         if (state == BluetoothAdapter.STATE_ON) {
207             // if mProfileManager hasn't been constructed yet, it will
208             // get the adapter UUIDs in its constructor when it is.
209             if (mProfileManager != null) {
210                 mProfileManager.setBluetoothStateOn();
211             }
212         }
213     }
214 
215     // Returns true if the state changed; false otherwise.
syncBluetoothState()216     boolean syncBluetoothState() {
217         int currentState = mAdapter.getState();
218         if (currentState != mState) {
219             setBluetoothStateInt(mAdapter.getState());
220             return true;
221         }
222         return false;
223     }
224 
setBluetoothEnabled(boolean enabled)225     public boolean setBluetoothEnabled(boolean enabled) {
226         boolean success = enabled
227                 ? mAdapter.enable()
228                 : mAdapter.disable();
229 
230         if (success) {
231             setBluetoothStateInt(enabled
232                 ? BluetoothAdapter.STATE_TURNING_ON
233                 : BluetoothAdapter.STATE_TURNING_OFF);
234         } else {
235             if (Utils.V) {
236                 Log.v(TAG, "setBluetoothEnabled call, manager didn't return " +
237                         "success for enabled: " + enabled);
238             }
239 
240             syncBluetoothState();
241         }
242         return success;
243     }
244 
getRemoteDevice(String address)245     public BluetoothDevice getRemoteDevice(String address) {
246         return mAdapter.getRemoteDevice(address);
247     }
248 
getMaxConnectedAudioDevices()249     public int getMaxConnectedAudioDevices() {
250         return mAdapter.getMaxConnectedAudioDevices();
251     }
252 
getSupportedProfiles()253     public List<Integer> getSupportedProfiles() {
254         return mAdapter.getSupportedProfiles();
255     }
256 }
257