1 /*
2  * Copyright (C) 2012 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.bluetooth.btservice;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothProfile;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.os.ParcelUuid;
25 import android.os.UserHandle;
26 import android.util.Log;
27 import android.util.Pair;
28 
29 import com.android.bluetooth.Utils;
30 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
31 
32 import java.util.HashMap;
33 import java.util.ArrayList;
34 import java.util.concurrent.CopyOnWriteArrayList;
35 
36 class AdapterProperties {
37     private static final boolean DBG = true;
38     private static final boolean VDBG = false;
39     private static final String TAG = "BluetoothAdapterProperties";
40 
41     private static final int BD_ADDR_LEN = 6; // 6 bytes
42     private String mName;
43     private byte[] mAddress;
44     private int mBluetoothClass;
45     private int mScanMode;
46     private int mDiscoverableTimeout;
47     private ParcelUuid[] mUuids;
48     private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices = new CopyOnWriteArrayList<BluetoothDevice>();
49 
50     private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
51     private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState;
52 
53 
54     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
55     private int mState = BluetoothAdapter.STATE_OFF;
56 
57     private AdapterService mService;
58     private boolean mDiscovering;
59     private RemoteDevices mRemoteDevices;
60     private BluetoothAdapter mAdapter;
61     //TODO - all hw capabilities to be exposed as a class
62     private int mNumOfAdvertisementInstancesSupported;
63     private boolean mRpaOffloadSupported;
64     private int mNumOfOffloadedIrkSupported;
65     private int mNumOfOffloadedScanFilterSupported;
66     private int mOffloadedScanResultStorageBytes;
67     private boolean mIsActivityAndEnergyReporting;
68 
69     // Lock for all getters and setters.
70     // If finer grained locking is needer, more locks
71     // can be added here.
72     private Object mObject = new Object();
73 
AdapterProperties(AdapterService service)74     public AdapterProperties(AdapterService service) {
75         mService = service;
76         mAdapter = BluetoothAdapter.getDefaultAdapter();
77     }
init(RemoteDevices remoteDevices)78     public void init(RemoteDevices remoteDevices) {
79         if (mProfileConnectionState ==null) {
80             mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
81         } else {
82             mProfileConnectionState.clear();
83         }
84         mRemoteDevices = remoteDevices;
85     }
86 
cleanup()87     public void cleanup() {
88         mRemoteDevices = null;
89         if (mProfileConnectionState != null) {
90             mProfileConnectionState.clear();
91             mProfileConnectionState = null;
92         }
93         mService = null;
94         if (!mBondedDevices.isEmpty())
95             mBondedDevices.clear();
96     }
97 
98     @Override
clone()99     public Object clone() throws CloneNotSupportedException {
100         throw new CloneNotSupportedException();
101     }
102 
103     /**
104      * @return the mName
105      */
getName()106     String getName() {
107         synchronized (mObject) {
108             return mName;
109         }
110     }
111 
112     /**
113      * Set the local adapter property - name
114      * @param name the name to set
115      */
setName(String name)116     boolean setName(String name) {
117         synchronized (mObject) {
118             return mService.setAdapterPropertyNative(
119                     AbstractionLayer.BT_PROPERTY_BDNAME, name.getBytes());
120         }
121     }
122 
123     /**
124      * @return the mClass
125      */
getBluetoothClass()126     int getBluetoothClass() {
127         synchronized (mObject) {
128             return mBluetoothClass;
129         }
130     }
131 
132     /**
133      * @return the mScanMode
134      */
getScanMode()135     int getScanMode() {
136         synchronized (mObject) {
137             return mScanMode;
138         }
139     }
140 
141     /**
142      * Set the local adapter property - scanMode
143      *
144      * @param scanMode the ScanMode to set
145      */
setScanMode(int scanMode)146     boolean setScanMode(int scanMode) {
147         synchronized (mObject) {
148             return mService.setAdapterPropertyNative(
149                     AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE, Utils.intToByteArray(scanMode));
150         }
151     }
152 
153     /**
154      * @return the mUuids
155      */
getUuids()156     ParcelUuid[] getUuids() {
157         synchronized (mObject) {
158             return mUuids;
159         }
160     }
161 
162     /**
163      * Set local adapter UUIDs.
164      *
165      * @param uuids the uuids to be set.
166      */
setUuids(ParcelUuid[] uuids)167     boolean setUuids(ParcelUuid[] uuids) {
168         synchronized (mObject) {
169             return mService.setAdapterPropertyNative(
170                     AbstractionLayer.BT_PROPERTY_UUIDS, Utils.uuidsToByteArray(uuids));
171         }
172     }
173 
174     /**
175      * @return the mAddress
176      */
getAddress()177     byte[] getAddress() {
178         synchronized (mObject) {
179             return mAddress;
180         }
181     }
182 
183     /**
184      * @param mConnectionState the mConnectionState to set
185      */
setConnectionState(int mConnectionState)186     void setConnectionState(int mConnectionState) {
187         synchronized (mObject) {
188             this.mConnectionState = mConnectionState;
189         }
190     }
191 
192     /**
193      * @return the mConnectionState
194      */
getConnectionState()195     int getConnectionState() {
196         synchronized (mObject) {
197             return mConnectionState;
198         }
199     }
200 
201     /**
202      * @param mState the mState to set
203      */
setState(int mState)204     void setState(int mState) {
205         synchronized (mObject) {
206             debugLog("Setting state to " + mState);
207             this.mState = mState;
208         }
209     }
210 
211     /**
212      * @return the mState
213      */
getState()214     int getState() {
215         /* remove the lock to work around a platform deadlock problem */
216         /* and also for read access, it is safe to remove the lock to save CPU power */
217         return mState;
218     }
219 
220     /**
221      * @return the mNumOfAdvertisementInstancesSupported
222      */
getNumOfAdvertisementInstancesSupported()223     int getNumOfAdvertisementInstancesSupported() {
224         return mNumOfAdvertisementInstancesSupported;
225     }
226 
227     /**
228      * @return the mRpaOffloadSupported
229      */
isRpaOffloadSupported()230     boolean isRpaOffloadSupported() {
231         return mRpaOffloadSupported;
232     }
233 
234     /**
235      * @return the mNumOfOffloadedIrkSupported
236      */
getNumOfOffloadedIrkSupported()237     int getNumOfOffloadedIrkSupported() {
238         return mNumOfOffloadedIrkSupported;
239     }
240 
241     /**
242      * @return the mNumOfOffloadedScanFilterSupported
243      */
getNumOfOffloadedScanFilterSupported()244     int getNumOfOffloadedScanFilterSupported() {
245         return mNumOfOffloadedScanFilterSupported;
246     }
247 
248     /**
249      * @return the mOffloadedScanResultStorageBytes
250      */
getOffloadedScanResultStorage()251     int getOffloadedScanResultStorage() {
252         return mOffloadedScanResultStorageBytes;
253     }
254 
255     /**
256      * @return tx/rx/idle activity and energy info
257      */
isActivityAndEnergyReportingSupported()258     boolean isActivityAndEnergyReportingSupported() {
259         return mIsActivityAndEnergyReporting;
260     }
261     /**
262      * @return the mBondedDevices
263      */
getBondedDevices()264     BluetoothDevice[] getBondedDevices() {
265         BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0];
266         synchronized (mObject) {
267             if(mBondedDevices.isEmpty())
268                 return (new BluetoothDevice[0]);
269 
270             try {
271                 bondedDeviceList = mBondedDevices.toArray(bondedDeviceList);
272                 infoLog("getBondedDevices: length="+bondedDeviceList.length);
273                 return bondedDeviceList;
274             } catch(ArrayStoreException ee) {
275                 errorLog("Error retrieving bonded device array");
276                 return (new BluetoothDevice[0]);
277             }
278         }
279     }
280     // This function shall be invoked from BondStateMachine whenever the bond
281     // state changes.
onBondStateChanged(BluetoothDevice device, int state)282     void onBondStateChanged(BluetoothDevice device, int state)
283     {
284         if(device == null)
285             return;
286         try {
287             byte[] addrByte = Utils.getByteAddress(device);
288             DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
289             if (prop == null)
290                 prop = mRemoteDevices.addDeviceProperties(addrByte);
291             prop.setBondState(state);
292 
293             if (state == BluetoothDevice.BOND_BONDED) {
294                 // add if not already in list
295                 if(!mBondedDevices.contains(device)) {
296                     debugLog("Adding bonded device:" +  device);
297                     mBondedDevices.add(device);
298                 }
299             } else if (state == BluetoothDevice.BOND_NONE) {
300                 // remove device from list
301                 if (mBondedDevices.remove(device))
302                     debugLog("Removing bonded device:" +  device);
303                 else
304                     debugLog("Failed to remove device: " + device);
305             }
306         }
307         catch(Exception ee) {
308             Log.e(TAG, "Exception in onBondStateChanged : ", ee);
309         }
310     }
311 
getDiscoverableTimeout()312     int getDiscoverableTimeout() {
313         synchronized (mObject) {
314             return mDiscoverableTimeout;
315         }
316     }
317 
setDiscoverableTimeout(int timeout)318     boolean setDiscoverableTimeout(int timeout) {
319         synchronized (mObject) {
320             return mService.setAdapterPropertyNative(
321                     AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
322                     Utils.intToByteArray(timeout));
323         }
324     }
325 
getProfileConnectionState(int profile)326     int getProfileConnectionState(int profile) {
327         synchronized (mObject) {
328             Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
329             if (p != null) return p.first;
330             return BluetoothProfile.STATE_DISCONNECTED;
331         }
332     }
333 
isDiscovering()334     boolean isDiscovering() {
335         synchronized (mObject) {
336             return mDiscovering;
337         }
338     }
339 
sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState)340     void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) {
341         if (!validateProfileConnectionState(state) ||
342                 !validateProfileConnectionState(prevState)) {
343             // Previously, an invalid state was broadcast anyway,
344             // with the invalid state converted to -1 in the intent.
345             // Better to log an error and not send an intent with
346             // invalid contents or set mAdapterConnectionState to -1.
347             errorLog("Error in sendConnectionStateChange: "
348                     + "prevState " + prevState + " state " + state);
349             return;
350         }
351 
352         synchronized (mObject) {
353             updateProfileConnectionState(profile, state, prevState);
354 
355             if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
356                 setConnectionState(state);
357 
358                 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
359                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
360                 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
361                         convertToAdapterState(state));
362                 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
363                         convertToAdapterState(prevState));
364                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
365                 mService.sendBroadcastAsUser(intent, UserHandle.ALL,
366                         mService.BLUETOOTH_PERM);
367                 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
368                         + prevState + " -> " + state);
369             }
370         }
371     }
372 
validateProfileConnectionState(int state)373     private boolean validateProfileConnectionState(int state) {
374         return (state == BluetoothProfile.STATE_DISCONNECTED ||
375                 state == BluetoothProfile.STATE_CONNECTING ||
376                 state == BluetoothProfile.STATE_CONNECTED ||
377                 state == BluetoothProfile.STATE_DISCONNECTING);
378     }
379 
380 
convertToAdapterState(int state)381     private int convertToAdapterState(int state) {
382         switch (state) {
383             case BluetoothProfile.STATE_DISCONNECTED:
384                 return BluetoothAdapter.STATE_DISCONNECTED;
385             case BluetoothProfile.STATE_DISCONNECTING:
386                 return BluetoothAdapter.STATE_DISCONNECTING;
387             case BluetoothProfile.STATE_CONNECTED:
388                 return BluetoothAdapter.STATE_CONNECTED;
389             case BluetoothProfile.STATE_CONNECTING:
390                 return BluetoothAdapter.STATE_CONNECTING;
391         }
392         Log.e(TAG, "Error in convertToAdapterState");
393         return -1;
394     }
395 
updateCountersAndCheckForConnectionStateChange(int state, int prevState)396     private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
397         switch (prevState) {
398             case BluetoothProfile.STATE_CONNECTING:
399                 mProfilesConnecting--;
400                 break;
401 
402             case BluetoothProfile.STATE_CONNECTED:
403                 mProfilesConnected--;
404                 break;
405 
406             case BluetoothProfile.STATE_DISCONNECTING:
407                 mProfilesDisconnecting--;
408                 break;
409         }
410 
411         switch (state) {
412             case BluetoothProfile.STATE_CONNECTING:
413                 mProfilesConnecting++;
414                 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
415 
416             case BluetoothProfile.STATE_CONNECTED:
417                 mProfilesConnected++;
418                 return (mProfilesConnected == 1);
419 
420             case BluetoothProfile.STATE_DISCONNECTING:
421                 mProfilesDisconnecting++;
422                 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
423 
424             case BluetoothProfile.STATE_DISCONNECTED:
425                 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
426 
427             default:
428                 return true;
429         }
430     }
431 
updateProfileConnectionState(int profile, int newState, int oldState)432     private void updateProfileConnectionState(int profile, int newState, int oldState) {
433         // mProfileConnectionState is a hashmap -
434         // <Integer, Pair<Integer, Integer>>
435         // The key is the profile, the value is a pair. first element
436         // is the state and the second element is the number of devices
437         // in that state.
438         int numDev = 1;
439         int newHashState = newState;
440         boolean update = true;
441 
442         // The following conditions are considered in this function:
443         // 1. If there is no record of profile and state - update
444         // 2. If a new device's state is current hash state - increment
445         //    number of devices in the state.
446         // 3. If a state change has happened to Connected or Connecting
447         //    (if current state is not connected), update.
448         // 4. If numDevices is 1 and that device state is being updated, update
449         // 5. If numDevices is > 1 and one of the devices is changing state,
450         //    decrement numDevices but maintain oldState if it is Connected or
451         //    Connecting
452         Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
453         if (stateNumDev != null) {
454             int currHashState = stateNumDev.first;
455             numDev = stateNumDev.second;
456 
457             if (newState == currHashState) {
458                 numDev ++;
459             } else if (newState == BluetoothProfile.STATE_CONNECTED ||
460                    (newState == BluetoothProfile.STATE_CONNECTING &&
461                     currHashState != BluetoothProfile.STATE_CONNECTED)) {
462                  numDev = 1;
463             } else if (numDev == 1 && oldState == currHashState) {
464                  update = true;
465             } else if (numDev > 1 && oldState == currHashState) {
466                  numDev --;
467 
468                  if (currHashState == BluetoothProfile.STATE_CONNECTED ||
469                      currHashState == BluetoothProfile.STATE_CONNECTING) {
470                     newHashState = currHashState;
471                  }
472             } else {
473                  update = false;
474             }
475         }
476 
477         if (update) {
478             mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
479                     numDev));
480         }
481     }
482 
adapterPropertyChangedCallback(int[] types, byte[][] values)483     void adapterPropertyChangedCallback(int[] types, byte[][] values) {
484         Intent intent;
485         int type;
486         byte[] val;
487         for (int i = 0; i < types.length; i++) {
488             val = values[i];
489             type = types[i];
490             infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
491             synchronized (mObject) {
492                 switch (type) {
493                     case AbstractionLayer.BT_PROPERTY_BDNAME:
494                         mName = new String(val);
495                         intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
496                         intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName);
497                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
498                         mService.sendBroadcastAsUser(intent, UserHandle.ALL,
499                                  mService.BLUETOOTH_PERM);
500                         debugLog("Name is: " + mName);
501                         break;
502                     case AbstractionLayer.BT_PROPERTY_BDADDR:
503                         mAddress = val;
504                         debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress));
505                         break;
506                     case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
507                         mBluetoothClass = Utils.byteArrayToInt(val, 0);
508                         debugLog("BT Class:" + mBluetoothClass);
509                         break;
510                     case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
511                         int mode = Utils.byteArrayToInt(val, 0);
512                         mScanMode = mService.convertScanModeFromHal(mode);
513                         intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
514                         intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
515                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
516                         mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
517                         debugLog("Scan Mode:" + mScanMode);
518                         if (mBluetoothDisabling) {
519                             mBluetoothDisabling=false;
520                             mService.startBluetoothDisable();
521                         }
522                         break;
523                     case AbstractionLayer.BT_PROPERTY_UUIDS:
524                         mUuids = Utils.byteArrayToUuid(val);
525                         break;
526                     case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
527                         int number = val.length/BD_ADDR_LEN;
528                         byte[] addrByte = new byte[BD_ADDR_LEN];
529                         for (int j = 0; j < number; j++) {
530                             System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
531                             onBondStateChanged(mAdapter.getRemoteDevice(
532                                                Utils.getAddressStringFromByte(addrByte)),
533                                                BluetoothDevice.BOND_BONDED);
534                         }
535                         break;
536                     case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
537                         mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
538                         debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
539                         break;
540 
541                     case AbstractionLayer.BT_PROPERTY_LOCAL_LE_FEATURES:
542                         updateFeatureSupport(val);
543                         break;
544 
545                     default:
546                         errorLog("Property change not handled in Java land:" + type);
547                 }
548             }
549         }
550     }
551 
updateFeatureSupport(byte[] val)552     void updateFeatureSupport(byte[] val) {
553         mNumOfAdvertisementInstancesSupported = (0xFF & ((int)val[1]));
554         mRpaOffloadSupported = ((0xFF & ((int)val[2]))!= 0);
555         mNumOfOffloadedIrkSupported =  (0xFF & ((int)val[3]));
556         mNumOfOffloadedScanFilterSupported = (0xFF & ((int)val[4]));
557         mOffloadedScanResultStorageBytes = ((0xFF & ((int)val[6])) << 8)
558                             + (0xFF & ((int)val[5]));
559         mIsActivityAndEnergyReporting = ((0xFF & ((int)val[7])) != 0);
560 
561         Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller"
562                 + " mNumOfAdvertisementInstancesSupported = "
563                 + mNumOfAdvertisementInstancesSupported
564                 + " mRpaOffloadSupported = " + mRpaOffloadSupported
565                 + " mNumOfOffloadedIrkSupported = "
566                 + mNumOfOffloadedIrkSupported
567                 + " mNumOfOffloadedScanFilterSupported = "
568                 + mNumOfOffloadedScanFilterSupported
569                 + " mOffloadedScanResultStorageBytes= "
570                 + mOffloadedScanResultStorageBytes
571                 + " mIsActivityAndEnergyReporting = "
572                 + mIsActivityAndEnergyReporting);
573     }
574 
onBluetoothReady()575     void onBluetoothReady() {
576         Log.d(TAG, "ScanMode =  " + mScanMode );
577         Log.d(TAG, "State =  " + getState() );
578 
579         // When BT is being turned on, all adapter properties will be sent in 1
580         // callback. At this stage, set the scan mode.
581         synchronized (mObject) {
582             if (getState() == BluetoothAdapter.STATE_TURNING_ON &&
583                     mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
584                     /* mDiscoverableTimeout is part of the
585                        adapterPropertyChangedCallback received before
586                        onBluetoothReady */
587                     if (mDiscoverableTimeout != 0)
588                       setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
589                     else /* if timeout == never (0) at startup */
590                       setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
591                     /* though not always required, this keeps NV up-to date on first-boot after flash */
592                     setDiscoverableTimeout(mDiscoverableTimeout);
593             }
594         }
595     }
596 
597     private boolean mBluetoothDisabling=false;
598 
onBluetoothDisable()599     void onBluetoothDisable() {
600         // When BT disable is invoked, set the scan_mode to NONE
601         // so no incoming connections are possible
602 
603         //Set flag to indicate we are disabling. When property change of scan mode done
604         //continue with disable sequence
605         debugLog("onBluetoothDisable()");
606         mBluetoothDisabling = true;
607         if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
608             setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
609         }
610     }
discoveryStateChangeCallback(int state)611     void discoveryStateChangeCallback(int state) {
612         infoLog("Callback:discoveryStateChangeCallback with state:" + state);
613         synchronized (mObject) {
614             Intent intent;
615             if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
616                 mDiscovering = false;
617                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
618                 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
619             } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
620                 mDiscovering = true;
621                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
622                 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
623             }
624         }
625     }
626 
infoLog(String msg)627     private void infoLog(String msg) {
628         if (VDBG) Log.i(TAG, msg);
629     }
630 
debugLog(String msg)631     private void debugLog(String msg) {
632         if (DBG) Log.d(TAG, msg);
633     }
634 
errorLog(String msg)635     private void errorLog(String msg) {
636         Log.e(TAG, msg);
637     }
638 }
639