• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 /**
18  * TODO: Move this to
19  * java/services/com/android/server/BluetoothService.java
20  * and make the contructor package private again.
21  *
22  * @hide
23  */
24 
25 package android.server;
26 
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.BluetoothClass;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothHeadset;
31 import android.bluetooth.BluetoothSocket;
32 import android.bluetooth.BluetoothUuid;
33 import android.bluetooth.IBluetooth;
34 import android.bluetooth.IBluetoothCallback;
35 import android.os.ParcelUuid;
36 import android.content.BroadcastReceiver;
37 import android.content.ContentResolver;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import android.os.Binder;
42 import android.os.IBinder;
43 import android.os.Handler;
44 import android.os.Message;
45 import android.os.RemoteException;
46 import android.os.ServiceManager;
47 import android.os.SystemService;
48 import android.provider.Settings;
49 import android.util.Log;
50 
51 import com.android.internal.app.IBatteryStats;
52 
53 import java.io.FileDescriptor;
54 import java.io.PrintWriter;
55 import java.io.UnsupportedEncodingException;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.Map;
61 
62 public class BluetoothService extends IBluetooth.Stub {
63     private static final String TAG = "BluetoothService";
64     private static final boolean DBG = false;
65 
66     private int mNativeData;
67     private BluetoothEventLoop mEventLoop;
68     private IntentFilter mIntentFilter;
69     private boolean mIsAirplaneSensitive;
70     private int mBluetoothState;
71     private boolean mRestart = false;  // need to call enable() after disable()
72     private boolean mIsDiscovering;
73 
74     private BluetoothAdapter mAdapter;  // constant after init()
75     private final BondState mBondState = new BondState();  // local cache of bondings
76     private final IBatteryStats mBatteryStats;
77     private final Context mContext;
78 
79     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
80     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
81 
82     private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
83     private static final int MESSAGE_FINISH_DISABLE = 2;
84     private static final int MESSAGE_UUID_INTENT = 3;
85     private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
86 
87     // The timeout used to sent the UUIDs Intent
88     // This timeout should be greater than the page timeout
89     private static final int UUID_INTENT_DELAY = 6000;
90 
91     /** Always retrieve RFCOMM channel for these SDP UUIDs */
92     private static final ParcelUuid[] RFCOMM_UUIDS = {
93             BluetoothUuid.Handsfree,
94             BluetoothUuid.HSP,
95             BluetoothUuid.ObexObjectPush };
96 
97 
98     private final Map<String, String> mAdapterProperties;
99     private final HashMap<String, Map<String, String>> mDeviceProperties;
100 
101     private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
102     private final ArrayList<String> mUuidIntentTracker;
103     private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
104 
105     private final HashMap<Integer, Integer> mServiceRecordToPid;
106 
107     private static class RemoteService {
108         public String address;
109         public ParcelUuid uuid;
RemoteService(String address, ParcelUuid uuid)110         public RemoteService(String address, ParcelUuid uuid) {
111             this.address = address;
112             this.uuid = uuid;
113         }
114         @Override
equals(Object o)115         public boolean equals(Object o) {
116             if (o instanceof RemoteService) {
117                 RemoteService service = (RemoteService)o;
118                 return address.equals(service.address) && uuid.equals(service.uuid);
119             }
120             return false;
121         }
122     }
123 
124     static {
classInitNative()125         classInitNative();
126     }
127 
BluetoothService(Context context)128     public BluetoothService(Context context) {
129         mContext = context;
130 
131         // Need to do this in place of:
132         // mBatteryStats = BatteryStatsService.getService();
133         // Since we can not import BatteryStatsService from here. This class really needs to be
134         // moved to java/services/com/android/server/
135         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
136 
137         initializeNativeDataNative();
138 
139         if (isEnabledNative() == 1) {
140             Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
141             disableNative();
142         }
143 
144         mBluetoothState = BluetoothAdapter.STATE_OFF;
145         mIsDiscovering = false;
146         mAdapterProperties = new HashMap<String, String>();
147         mDeviceProperties = new HashMap<String, Map<String,String>>();
148 
149         mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
150         mUuidIntentTracker = new ArrayList<String>();
151         mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
152         mServiceRecordToPid = new HashMap<Integer, Integer>();
153         registerForAirplaneMode();
154     }
155 
initAfterRegistration()156     public synchronized void initAfterRegistration() {
157         mAdapter = BluetoothAdapter.getDefaultAdapter();
158         mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
159     }
160 
161     @Override
finalize()162     protected void finalize() throws Throwable {
163         if (mIsAirplaneSensitive) {
164             mContext.unregisterReceiver(mReceiver);
165         }
166         try {
167             cleanupNativeDataNative();
168         } finally {
169             super.finalize();
170         }
171     }
172 
isEnabled()173     public boolean isEnabled() {
174         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
175         return mBluetoothState == BluetoothAdapter.STATE_ON;
176     }
177 
getBluetoothState()178     public int getBluetoothState() {
179         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
180         return mBluetoothState;
181     }
182 
183 
184     /**
185      * Bring down bluetooth and disable BT in settings. Returns true on success.
186      */
disable()187     public boolean disable() {
188         return disable(true);
189     }
190 
191     /**
192      * Bring down bluetooth. Returns true on success.
193      *
194      * @param saveSetting If true, persist the new setting
195      */
disable(boolean saveSetting)196     public synchronized boolean disable(boolean saveSetting) {
197         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
198 
199         switch (mBluetoothState) {
200         case BluetoothAdapter.STATE_OFF:
201             return true;
202         case BluetoothAdapter.STATE_ON:
203             break;
204         default:
205             return false;
206         }
207         if (mEnableThread != null && mEnableThread.isAlive()) {
208             return false;
209         }
210         setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
211         mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
212 
213         // Allow 3 seconds for profiles to gracefully disconnect
214         // TODO: Introduce a callback mechanism so that each profile can notify
215         // BluetoothService when it is done shutting down
216         mHandler.sendMessageDelayed(
217                 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
218         return true;
219     }
220 
221 
finishDisable(boolean saveSetting)222     private synchronized void finishDisable(boolean saveSetting) {
223         if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
224             return;
225         }
226         mEventLoop.stop();
227         tearDownNativeDataNative();
228         disableNative();
229 
230         // mark in progress bondings as cancelled
231         for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
232             mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
233                                     BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
234         }
235 
236         // update mode
237         Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
238         intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
239         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
240 
241         mIsDiscovering = false;
242         mAdapterProperties.clear();
243         mServiceRecordToPid.clear();
244 
245         if (saveSetting) {
246             persistBluetoothOnSetting(false);
247         }
248 
249         setBluetoothState(BluetoothAdapter.STATE_OFF);
250 
251         // Log bluetooth off to battery stats.
252         long ident = Binder.clearCallingIdentity();
253         try {
254             mBatteryStats.noteBluetoothOff();
255         } catch (RemoteException e) {
256         } finally {
257             Binder.restoreCallingIdentity(ident);
258         }
259 
260         if (mRestart) {
261             mRestart = false;
262             enable();
263         }
264     }
265 
266     /** Bring up BT and persist BT on in settings */
enable()267     public boolean enable() {
268         return enable(true);
269     }
270 
271     /**
272      * Enable this Bluetooth device, asynchronously.
273      * This turns on/off the underlying hardware.
274      *
275      * @param saveSetting If true, persist the new state of BT in settings
276      * @return True on success (so far)
277      */
enable(boolean saveSetting)278     public synchronized boolean enable(boolean saveSetting) {
279         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
280                                                 "Need BLUETOOTH_ADMIN permission");
281 
282         // Airplane mode can prevent Bluetooth radio from being turned on.
283         if (mIsAirplaneSensitive && isAirplaneModeOn()) {
284             return false;
285         }
286         if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
287             return false;
288         }
289         if (mEnableThread != null && mEnableThread.isAlive()) {
290             return false;
291         }
292         setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
293         mEnableThread = new EnableThread(saveSetting);
294         mEnableThread.start();
295         return true;
296     }
297 
298     /** Forcibly restart Bluetooth if it is on */
restart()299     /* package */ synchronized void restart() {
300         if (mBluetoothState != BluetoothAdapter.STATE_ON) {
301             return;
302         }
303         mRestart = true;
304         if (!disable(false)) {
305             mRestart = false;
306         }
307     }
308 
setBluetoothState(int state)309     private synchronized void setBluetoothState(int state) {
310         if (state == mBluetoothState) {
311             return;
312         }
313 
314         if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
315 
316         Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
317         intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
318         intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
319         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
320 
321         mBluetoothState = state;
322 
323         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
324     }
325 
326     private final Handler mHandler = new Handler() {
327         @Override
328         public void handleMessage(Message msg) {
329             switch (msg.what) {
330             case MESSAGE_REGISTER_SDP_RECORDS:
331                 if (!isEnabled()) {
332                     return;
333                 }
334                 // SystemService.start() forks sdptool to register service
335                 // records. It can fail to register some records if it is
336                 // forked multiple times in a row, probably because there is
337                 // some race in sdptool or bluez when operated in parallel.
338                 // As a workaround, delay 500ms between each fork of sdptool.
339                 // TODO: Don't fork sdptool in order to regsiter service
340                 // records, use a DBUS call instead.
341                 switch (msg.arg1) {
342                 case 1:
343                     Log.d(TAG, "Registering hsag record");
344                     SystemService.start("hsag");
345                     mHandler.sendMessageDelayed(
346                             mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500);
347                     break;
348                 case 2:
349                     Log.d(TAG, "Registering hfag record");
350                     SystemService.start("hfag");
351                     mHandler.sendMessageDelayed(
352                             mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500);
353                     break;
354                 case 3:
355                     Log.d(TAG, "Registering opush record");
356                     SystemService.start("opush");
357                     mHandler.sendMessageDelayed(
358                             mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500);
359                     break;
360                 case 4:
361                     Log.d(TAG, "Registering pbap record");
362                     SystemService.start("pbap");
363                     break;
364                 }
365                 break;
366             case MESSAGE_FINISH_DISABLE:
367                 finishDisable(msg.arg1 != 0);
368                 break;
369             case MESSAGE_UUID_INTENT:
370                 String address = (String)msg.obj;
371                 if (address != null) {
372                     sendUuidIntent(address);
373                     makeServiceChannelCallbacks(address);
374                 }
375                 break;
376             case MESSAGE_DISCOVERABLE_TIMEOUT:
377                 int mode = msg.arg1;
378                 if (isEnabled()) {
379                     // TODO: Switch back to the previous scan mode
380                     // This is ok for now, because we only use
381                     // CONNECTABLE and CONNECTABLE_DISCOVERABLE
382                     setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
383                 }
384                 break;
385             }
386         }
387     };
388 
389     private EnableThread mEnableThread;
390 
391     private class EnableThread extends Thread {
392         private final boolean mSaveSetting;
EnableThread(boolean saveSetting)393         public EnableThread(boolean saveSetting) {
394             mSaveSetting = saveSetting;
395         }
run()396         public void run() {
397             boolean res = (enableNative() == 0);
398             if (res) {
399                 int retryCount = 2;
400                 boolean running = false;
401                 while ((retryCount-- > 0) && !running) {
402                     mEventLoop.start();
403                     // it may take a momement for the other thread to do its
404                     // thing.  Check periodically for a while.
405                     int pollCount = 5;
406                     while ((pollCount-- > 0) && !running) {
407                         if (mEventLoop.isEventLoopRunning()) {
408                             running = true;
409                             break;
410                         }
411                         try {
412                             Thread.sleep(100);
413                         } catch (InterruptedException e) {}
414                     }
415                 }
416                 if (!running) {
417                     log("bt EnableThread giving up");
418                     res = false;
419                     disableNative();
420                 }
421             }
422 
423 
424             if (res) {
425                 if (!setupNativeDataNative()) {
426                     return;
427                 }
428                 if (mSaveSetting) {
429                     persistBluetoothOnSetting(true);
430                 }
431                 mIsDiscovering = false;
432                 mBondState.loadBondState();
433                 mHandler.sendMessageDelayed(
434                         mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
435 
436                 // Log bluetooth on to battery stats.
437                 long ident = Binder.clearCallingIdentity();
438                 try {
439                     mBatteryStats.noteBluetoothOn();
440                 } catch (RemoteException e) {
441                 } finally {
442                     Binder.restoreCallingIdentity(ident);
443                 }
444             }
445 
446             mEnableThread = null;
447 
448             setBluetoothState(res ?
449                               BluetoothAdapter.STATE_ON :
450                               BluetoothAdapter.STATE_OFF);
451 
452             if (res) {
453                 // Update mode
454                 String[] propVal = {"Pairable", getProperty("Pairable")};
455                 mEventLoop.onPropertyChanged(propVal);
456             }
457 
458             if (mIsAirplaneSensitive && isAirplaneModeOn()) {
459                 disable(false);
460             }
461 
462         }
463     }
464 
persistBluetoothOnSetting(boolean bluetoothOn)465     private void persistBluetoothOnSetting(boolean bluetoothOn) {
466         long origCallerIdentityToken = Binder.clearCallingIdentity();
467         Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
468                 bluetoothOn ? 1 : 0);
469         Binder.restoreCallingIdentity(origCallerIdentityToken);
470     }
471 
getBondState()472     /* package */ BondState getBondState() {
473         return mBondState;
474     }
475 
476     /** local cache of bonding state.
477     /* we keep our own state to track the intermediate state BONDING, which
478     /* bluez does not track.
479      * All addreses must be passed in upper case.
480      */
481     public class BondState {
482         private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
483         private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
484         private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
485         // List of all the vendor_id prefix of Bluetooth addresses for
486         // which auto pairing is not attempted.
487         // The following companies are included in the list below:
488         // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
489         // Parrot, Zhongshan General K-mate Electronics, Great Well
490         // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
491         // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
492         // Continental Automotive, Harman/Becker, Panasonic/Kyushu Ten,
493         // BMW (Motorola PCS)
494         private final ArrayList<String>  mAutoPairingAddressBlacklist =
495                 new ArrayList<String>(Arrays.asList(
496                         "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
497                         "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
498                         "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
499                         "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59",
500                         "00:0A:30", "00:1E:AE", "00:1C:D7", "00:80:F0", "00:12:8A"
501                         ));
502 
503         // List of names of Bluetooth devices for which auto pairing should be
504         // disabled.
505         private final ArrayList<String> mAutoPairingExactNameBlacklist =
506                 new ArrayList<String>(Arrays.asList(
507                         "Motorola IHF1000", "i.TechBlueBAND", "X5 Stereo v1.3"));
508 
509         private final ArrayList<String> mAutoPairingPartialNameBlacklist =
510                 new ArrayList<String>(Arrays.asList(
511                         "BMW", "Audi"));
512 
513         // If this is an outgoing connection, store the address.
514         // There can be only 1 pending outgoing connection at a time,
515         private String mPendingOutgoingBonding;
516 
setPendingOutgoingBonding(String address)517         private synchronized void setPendingOutgoingBonding(String address) {
518             mPendingOutgoingBonding = address;
519         }
520 
getPendingOutgoingBonding()521         public synchronized String getPendingOutgoingBonding() {
522             return mPendingOutgoingBonding;
523         }
524 
loadBondState()525         public synchronized void loadBondState() {
526             if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
527                 return;
528             }
529             String []bonds = null;
530             String val = getProperty("Devices");
531             if (val != null) {
532                 bonds = val.split(",");
533             }
534             if (bonds == null) {
535                 return;
536             }
537             mState.clear();
538             if (DBG) log("found " + bonds.length + " bonded devices");
539             for (String device : bonds) {
540                 mState.put(getAddressFromObjectPath(device).toUpperCase(),
541                         BluetoothDevice.BOND_BONDED);
542             }
543         }
544 
setBondState(String address, int state)545         public synchronized void setBondState(String address, int state) {
546             setBondState(address, state, 0);
547         }
548 
549         /** reason is ignored unless state == BOND_NOT_BONDED */
setBondState(String address, int state, int reason)550         public synchronized void setBondState(String address, int state, int reason) {
551             int oldState = getBondState(address);
552             if (oldState == state) {
553                 return;
554             }
555 
556             // Check if this was an pending outgoing bonding.
557             // If yes, reset the state.
558             if (oldState == BluetoothDevice.BOND_BONDING) {
559                 if (address.equals(mPendingOutgoingBonding)) {
560                     mPendingOutgoingBonding = null;
561                 }
562             }
563 
564             if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
565                          reason + ")");
566             Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
567             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
568             intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
569             intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
570             if (state == BluetoothDevice.BOND_NONE) {
571                 if (reason <= 0) {
572                     Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
573                           "invalid. Overriding reason code with BOND_RESULT_REMOVED");
574                     reason = BluetoothDevice.UNBOND_REASON_REMOVED;
575                 }
576                 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
577                 mState.remove(address);
578             } else {
579                 mState.put(address, state);
580             }
581 
582             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
583         }
584 
isAutoPairingBlacklisted(String address)585         public boolean isAutoPairingBlacklisted(String address) {
586             for (String blacklistAddress : mAutoPairingAddressBlacklist) {
587                 if (address.startsWith(blacklistAddress)) return true;
588             }
589 
590             String name = getRemoteName(address);
591             if (name != null) {
592                 for (String blacklistName : mAutoPairingExactNameBlacklist) {
593                     if (name.equals(blacklistName)) return true;
594                 }
595 
596                 for (String blacklistName : mAutoPairingPartialNameBlacklist) {
597                     if (name.startsWith(blacklistName)) return true;
598                 }
599             }
600             return false;
601         }
602 
getBondState(String address)603         public synchronized int getBondState(String address) {
604             Integer state = mState.get(address);
605             if (state == null) {
606                 return BluetoothDevice.BOND_NONE;
607             }
608             return state.intValue();
609         }
610 
listInState(int state)611         /*package*/ synchronized String[] listInState(int state) {
612             ArrayList<String> result = new ArrayList<String>(mState.size());
613             for (Map.Entry<String, Integer> e : mState.entrySet()) {
614                 if (e.getValue().intValue() == state) {
615                     result.add(e.getKey());
616                 }
617             }
618             return result.toArray(new String[result.size()]);
619         }
620 
addAutoPairingFailure(String address)621         public synchronized void addAutoPairingFailure(String address) {
622             if (!mAutoPairingFailures.contains(address)) {
623                 mAutoPairingFailures.add(address);
624             }
625         }
626 
isAutoPairingAttemptsInProgress(String address)627         public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
628             return getAttempt(address) != 0;
629         }
630 
clearPinAttempts(String address)631         public synchronized void clearPinAttempts(String address) {
632             mPinAttempt.remove(address);
633         }
634 
hasAutoPairingFailed(String address)635         public synchronized boolean hasAutoPairingFailed(String address) {
636             return mAutoPairingFailures.contains(address);
637         }
638 
getAttempt(String address)639         public synchronized int getAttempt(String address) {
640             Integer attempt = mPinAttempt.get(address);
641             if (attempt == null) {
642                 return 0;
643             }
644             return attempt.intValue();
645         }
646 
attempt(String address)647         public synchronized void attempt(String address) {
648             Integer attempt = mPinAttempt.get(address);
649             int newAttempt;
650             if (attempt == null) {
651                 newAttempt = 1;
652             } else {
653                 newAttempt = attempt.intValue() + 1;
654             }
655             mPinAttempt.put(address, new Integer(newAttempt));
656         }
657 
658     }
659 
toBondStateString(int bondState)660     private static String toBondStateString(int bondState) {
661         switch (bondState) {
662         case BluetoothDevice.BOND_NONE:
663             return "not bonded";
664         case BluetoothDevice.BOND_BONDING:
665             return "bonding";
666         case BluetoothDevice.BOND_BONDED:
667             return "bonded";
668         default:
669             return "??????";
670         }
671     }
672 
isAdapterPropertiesEmpty()673     /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
674         return mAdapterProperties.isEmpty();
675     }
676 
getAllProperties()677     /*package*/synchronized void getAllProperties() {
678         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
679         mAdapterProperties.clear();
680 
681         String properties[] = (String [])getAdapterPropertiesNative();
682         // The String Array consists of key-value pairs.
683         if (properties == null) {
684             Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
685             return;
686         }
687 
688         for (int i = 0; i < properties.length; i++) {
689             String name = properties[i];
690             String newValue = null;
691             int len;
692             if (name == null) {
693                 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
694                 continue;
695             }
696             if (name.equals("Devices")) {
697                 StringBuilder str = new StringBuilder();
698                 len = Integer.valueOf(properties[++i]);
699                 for (int j = 0; j < len; j++) {
700                     str.append(properties[++i]);
701                     str.append(",");
702                 }
703                 if (len > 0) {
704                     newValue = str.toString();
705                 }
706             } else {
707                 newValue = properties[++i];
708             }
709             mAdapterProperties.put(name, newValue);
710         }
711 
712         // Add adapter object path property.
713         String adapterPath = getAdapterPathNative();
714         if (adapterPath != null)
715             mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
716     }
717 
setProperty(String name, String value)718     /* package */ synchronized void setProperty(String name, String value) {
719         mAdapterProperties.put(name, value);
720     }
721 
setName(String name)722     public synchronized boolean setName(String name) {
723         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
724                                                 "Need BLUETOOTH_ADMIN permission");
725         if (name == null) {
726             return false;
727         }
728         return setPropertyString("Name", name);
729     }
730 
731     //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
732     // Either have a single property function with Object as the parameter
733     // or have a function for each property and then obfuscate in the JNI layer.
734     // The following looks dirty.
setPropertyString(String key, String value)735     private boolean setPropertyString(String key, String value) {
736         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
737         return setAdapterPropertyStringNative(key, value);
738     }
739 
setPropertyInteger(String key, int value)740     private boolean setPropertyInteger(String key, int value) {
741         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
742         return setAdapterPropertyIntegerNative(key, value);
743     }
744 
setPropertyBoolean(String key, boolean value)745     private boolean setPropertyBoolean(String key, boolean value) {
746         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
747         return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
748     }
749 
750     /**
751      * Set the discoverability window for the device.  A timeout of zero
752      * makes the device permanently discoverable (if the device is
753      * discoverable).  Setting the timeout to a nonzero value does not make
754      * a device discoverable; you need to call setMode() to make the device
755      * explicitly discoverable.
756      *
757      * @param timeout_s The discoverable timeout in seconds.
758      */
setDiscoverableTimeout(int timeout)759     public synchronized boolean setDiscoverableTimeout(int timeout) {
760         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
761                                                 "Need BLUETOOTH_ADMIN permission");
762         return setPropertyInteger("DiscoverableTimeout", timeout);
763     }
764 
setScanMode(int mode, int duration)765     public synchronized boolean setScanMode(int mode, int duration) {
766         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
767                                                 "Need WRITE_SECURE_SETTINGS permission");
768         boolean pairable = false;
769         boolean discoverable = false;
770 
771         switch (mode) {
772         case BluetoothAdapter.SCAN_MODE_NONE:
773             mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
774             pairable = false;
775             discoverable = false;
776             break;
777         case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
778             mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
779             pairable = true;
780             discoverable = false;
781             break;
782         case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
783             mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
784             pairable = true;
785             discoverable = true;
786             Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
787             mHandler.sendMessageDelayed(msg, duration * 1000);
788             if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
789             break;
790         default:
791             Log.w(TAG, "Requested invalid scan mode " + mode);
792             return false;
793         }
794         setPropertyBoolean("Pairable", pairable);
795         setPropertyBoolean("Discoverable", discoverable);
796 
797         return true;
798     }
799 
getProperty(String name)800     /*package*/ synchronized String getProperty (String name) {
801         if (!mAdapterProperties.isEmpty())
802             return mAdapterProperties.get(name);
803         getAllProperties();
804         return mAdapterProperties.get(name);
805     }
806 
getAddress()807     public synchronized String getAddress() {
808         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
809         return getProperty("Address");
810     }
811 
getName()812     public synchronized String getName() {
813         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
814         return getProperty("Name");
815     }
816 
817     /**
818      * Returns the user-friendly name of a remote device.  This value is
819      * returned from our local cache, which is updated when onPropertyChange
820      * event is received.
821      * Do not expect to retrieve the updated remote name immediately after
822      * changing the name on the remote device.
823      *
824      * @param address Bluetooth address of remote device.
825      *
826      * @return The user-friendly name of the specified remote device.
827      */
getRemoteName(String address)828     public synchronized String getRemoteName(String address) {
829         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
830         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
831             return null;
832         }
833         return getRemoteDeviceProperty(address, "Name");
834     }
835 
836     /**
837      * Get the discoverability window for the device.  A timeout of zero
838      * means that the device is permanently discoverable (if the device is
839      * in the discoverable mode).
840      *
841      * @return The discoverability window of the device, in seconds.  A negative
842      *         value indicates an error.
843      */
getDiscoverableTimeout()844     public synchronized int getDiscoverableTimeout() {
845         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
846         String timeout = getProperty("DiscoverableTimeout");
847         if (timeout != null)
848            return Integer.valueOf(timeout);
849         else
850             return -1;
851     }
852 
getScanMode()853     public synchronized int getScanMode() {
854         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
855         if (!isEnabled())
856             return BluetoothAdapter.SCAN_MODE_NONE;
857 
858         boolean pairable = getProperty("Pairable").equals("true");
859         boolean discoverable = getProperty("Discoverable").equals("true");
860         return bluezStringToScanMode (pairable, discoverable);
861     }
862 
startDiscovery()863     public synchronized boolean startDiscovery() {
864         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
865                                                 "Need BLUETOOTH_ADMIN permission");
866         if (!isEnabled()) {
867             return false;
868         }
869         return startDiscoveryNative();
870     }
871 
cancelDiscovery()872     public synchronized boolean cancelDiscovery() {
873         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
874                                                 "Need BLUETOOTH_ADMIN permission");
875         return stopDiscoveryNative();
876     }
877 
isDiscovering()878     public synchronized boolean isDiscovering() {
879         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
880         return mIsDiscovering;
881     }
882 
setIsDiscovering(boolean isDiscovering)883     /* package */ void setIsDiscovering(boolean isDiscovering) {
884         mIsDiscovering = isDiscovering;
885     }
886 
createBond(String address)887     public synchronized boolean createBond(String address) {
888         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
889                                                 "Need BLUETOOTH_ADMIN permission");
890         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
891             return false;
892         }
893         address = address.toUpperCase();
894 
895         if (mBondState.getPendingOutgoingBonding() != null) {
896             log("Ignoring createBond(): another device is bonding");
897             // a different device is currently bonding, fail
898             return false;
899         }
900 
901         // Check for bond state only if we are not performing auto
902         // pairing exponential back-off attempts.
903         if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
904                 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
905             log("Ignoring createBond(): this device is already bonding or bonded");
906             return false;
907         }
908 
909         if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
910             return false;
911         }
912 
913         mBondState.setPendingOutgoingBonding(address);
914         mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
915 
916         return true;
917     }
918 
cancelBondProcess(String address)919     public synchronized boolean cancelBondProcess(String address) {
920         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
921                                                 "Need BLUETOOTH_ADMIN permission");
922         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
923             return false;
924         }
925         address = address.toUpperCase();
926         if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
927             return false;
928         }
929 
930         mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
931                                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
932         cancelDeviceCreationNative(address);
933         return true;
934     }
935 
removeBond(String address)936     public synchronized boolean removeBond(String address) {
937         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
938                                                 "Need BLUETOOTH_ADMIN permission");
939         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
940             return false;
941         }
942         return removeDeviceNative(getObjectPathFromAddress(address));
943     }
944 
listBonds()945     public synchronized String[] listBonds() {
946         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
947         return mBondState.listInState(BluetoothDevice.BOND_BONDED);
948     }
949 
getBondState(String address)950     public synchronized int getBondState(String address) {
951         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
952         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
953             return BluetoothDevice.ERROR;
954         }
955         return mBondState.getBondState(address.toUpperCase());
956     }
957 
isRemoteDeviceInCache(String address)958     /*package*/ boolean isRemoteDeviceInCache(String address) {
959         return (mDeviceProperties.get(address) != null);
960     }
961 
getRemoteDeviceProperties(String address)962     /*package*/ String[] getRemoteDeviceProperties(String address) {
963         String objectPath = getObjectPathFromAddress(address);
964         return (String [])getDevicePropertiesNative(objectPath);
965     }
966 
getRemoteDeviceProperty(String address, String property)967     /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
968         Map<String, String> properties = mDeviceProperties.get(address);
969         if (properties != null) {
970             return properties.get(property);
971         } else {
972             // Query for remote device properties, again.
973             // We will need to reload the cache when we switch Bluetooth on / off
974             // or if we crash.
975             if (updateRemoteDevicePropertiesCache(address))
976                 return getRemoteDeviceProperty(address, property);
977         }
978         Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
979         return null;
980     }
981 
updateRemoteDevicePropertiesCache(String address)982     /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
983         String[] propValues = getRemoteDeviceProperties(address);
984         if (propValues != null) {
985             addRemoteDeviceProperties(address, propValues);
986             return true;
987         }
988         return false;
989     }
990 
addRemoteDeviceProperties(String address, String[] properties)991     /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
992         /*
993          * We get a DeviceFound signal every time RSSI changes or name changes.
994          * Don't create a new Map object every time */
995         Map<String, String> propertyValues = mDeviceProperties.get(address);
996         if (propertyValues == null) {
997             propertyValues = new HashMap<String, String>();
998         }
999 
1000         for (int i = 0; i < properties.length; i++) {
1001             String name = properties[i];
1002             String newValue = null;
1003             int len;
1004             if (name == null) {
1005                 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1006                 continue;
1007             }
1008             if (name.equals("UUIDs") || name.equals("Nodes")) {
1009                 StringBuilder str = new StringBuilder();
1010                 len = Integer.valueOf(properties[++i]);
1011                 for (int j = 0; j < len; j++) {
1012                     str.append(properties[++i]);
1013                     str.append(",");
1014                 }
1015                 if (len > 0) {
1016                     newValue = str.toString();
1017                 }
1018             } else {
1019                 newValue = properties[++i];
1020             }
1021 
1022             propertyValues.put(name, newValue);
1023         }
1024         mDeviceProperties.put(address, propertyValues);
1025 
1026         // We have added a new remote device or updated its properties.
1027         // Also update the serviceChannel cache.
1028         updateDeviceServiceChannelCache(address);
1029     }
1030 
removeRemoteDeviceProperties(String address)1031     /* package */ void removeRemoteDeviceProperties(String address) {
1032         mDeviceProperties.remove(address);
1033     }
1034 
setRemoteDeviceProperty(String address, String name, String value)1035     /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1036                                                               String value) {
1037         Map <String, String> propVal = mDeviceProperties.get(address);
1038         if (propVal != null) {
1039             propVal.put(name, value);
1040             mDeviceProperties.put(address, propVal);
1041         } else {
1042             Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
1043         }
1044     }
1045 
1046     /**
1047      * Sets the remote device trust state.
1048      *
1049      * @return boolean to indicate operation success or fail
1050      */
setTrust(String address, boolean value)1051     public synchronized boolean setTrust(String address, boolean value) {
1052         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1053             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1054                     "Need BLUETOOTH_ADMIN permission");
1055             return false;
1056         }
1057 
1058         return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1059                 value ? 1 : 0);
1060     }
1061 
1062     /**
1063      * Gets the remote device trust state as boolean.
1064      * Note: this value may be
1065      * retrieved from cache if we retrieved the data before *
1066      *
1067      * @return boolean to indicate trust or untrust state
1068      */
getTrustState(String address)1069     public synchronized boolean getTrustState(String address) {
1070         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1071             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1072             return false;
1073         }
1074 
1075         String val = getRemoteDeviceProperty(address, "Trusted");
1076         if (val == null) {
1077             return false;
1078         } else {
1079             return val.equals("true") ? true : false;
1080         }
1081     }
1082 
1083     /**
1084      * Gets the remote major, minor classes encoded as a 32-bit
1085      * integer.
1086      *
1087      * Note: this value is retrieved from cache, because we get it during
1088      *       remote-device discovery.
1089      *
1090      * @return 32-bit integer encoding the remote major, minor, and service
1091      *         classes.
1092      */
getRemoteClass(String address)1093     public synchronized int getRemoteClass(String address) {
1094         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1095             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1096             return BluetoothClass.ERROR;
1097         }
1098         String val = getRemoteDeviceProperty(address, "Class");
1099         if (val == null)
1100             return BluetoothClass.ERROR;
1101         else {
1102             return Integer.valueOf(val);
1103         }
1104     }
1105 
1106 
1107     /**
1108      * Gets the UUIDs supported by the remote device
1109      *
1110      * @return array of 128bit ParcelUuids
1111      */
getRemoteUuids(String address)1112     public synchronized ParcelUuid[] getRemoteUuids(String address) {
1113         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1114         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1115             return null;
1116         }
1117         return getUuidFromCache(address);
1118     }
1119 
getUuidFromCache(String address)1120     private ParcelUuid[] getUuidFromCache(String address) {
1121         String value = getRemoteDeviceProperty(address, "UUIDs");
1122         if (value == null) return null;
1123 
1124         String[] uuidStrings = null;
1125         // The UUIDs are stored as a "," separated string.
1126         uuidStrings = value.split(",");
1127         ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1128 
1129         for (int i = 0; i < uuidStrings.length; i++) {
1130             uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1131         }
1132         return uuids;
1133     }
1134 
1135     /**
1136      * Connect and fetch new UUID's using SDP.
1137      * The UUID's found are broadcast as intents.
1138      * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1139      * a given uuid.
1140      * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1141      * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1142      * callback and broadcast intents.
1143      */
fetchRemoteUuids(String address, ParcelUuid uuid, IBluetoothCallback callback)1144     public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1145             IBluetoothCallback callback) {
1146         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1147         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1148             return false;
1149         }
1150 
1151         RemoteService service = new RemoteService(address, uuid);
1152         if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1153             // An SDP query for this address & uuid is already in progress
1154             // Do not add this callback for the uuid
1155             return false;
1156         }
1157 
1158         if (mUuidIntentTracker.contains(address)) {
1159             // An SDP query for this address is already in progress
1160             // Add this uuid onto the in-progress SDP query
1161             if (uuid != null) {
1162                 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1163             }
1164             return true;
1165         }
1166 
1167         boolean ret;
1168         if (getBondState(address) == BluetoothDevice.BOND_BONDED) {
1169             String path = getObjectPathFromAddress(address);
1170             if (path == null) return false;
1171 
1172             // Use an empty string for the UUID pattern
1173             ret = discoverServicesNative(path, "");
1174         } else {
1175             ret = createDeviceNative(address);
1176         }
1177 
1178         mUuidIntentTracker.add(address);
1179         if (uuid != null) {
1180             mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1181         }
1182 
1183         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1184         message.obj = address;
1185         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1186         return ret;
1187     }
1188 
1189     /**
1190      * Gets the rfcomm channel associated with the UUID.
1191      * Pulls records from the cache only.
1192      *
1193      * @param address Address of the remote device
1194      * @param uuid ParcelUuid of the service attribute
1195      *
1196      * @return rfcomm channel associated with the service attribute
1197      *         -1 on error
1198      */
getRemoteServiceChannel(String address, ParcelUuid uuid)1199     public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
1200         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1201         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1202             return BluetoothDevice.ERROR;
1203         }
1204         // Check if we are recovering from a crash.
1205         if (mDeviceProperties.isEmpty()) {
1206             if (!updateRemoteDevicePropertiesCache(address))
1207                 return -1;
1208         }
1209 
1210         Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1211         if (value != null && value.containsKey(uuid))
1212             return value.get(uuid);
1213         return -1;
1214     }
1215 
setPin(String address, byte[] pin)1216     public synchronized boolean setPin(String address, byte[] pin) {
1217         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1218                                                 "Need BLUETOOTH_ADMIN permission");
1219         if (pin == null || pin.length <= 0 || pin.length > 16 ||
1220             !BluetoothAdapter.checkBluetoothAddress(address)) {
1221             return false;
1222         }
1223         address = address.toUpperCase();
1224         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1225         if (data == null) {
1226             Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1227                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1228                   " or by bluez.\n");
1229             return false;
1230         }
1231         // bluez API wants pin as a string
1232         String pinString;
1233         try {
1234             pinString = new String(pin, "UTF8");
1235         } catch (UnsupportedEncodingException uee) {
1236             Log.e(TAG, "UTF8 not supported?!?");
1237             return false;
1238         }
1239         return setPinNative(address, pinString, data.intValue());
1240     }
1241 
setPasskey(String address, int passkey)1242     public synchronized boolean setPasskey(String address, int passkey) {
1243         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1244                                                 "Need BLUETOOTH_ADMIN permission");
1245         if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
1246             return false;
1247         }
1248         address = address.toUpperCase();
1249         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1250         if (data == null) {
1251             Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1252                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1253                   " or by bluez.\n");
1254             return false;
1255         }
1256         return setPasskeyNative(address, passkey, data.intValue());
1257     }
1258 
setPairingConfirmation(String address, boolean confirm)1259     public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1260         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1261                                                 "Need BLUETOOTH_ADMIN permission");
1262         address = address.toUpperCase();
1263         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1264         if (data == null) {
1265             Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1266                   "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1267                   " or by bluez.\n");
1268             return false;
1269         }
1270         return setPairingConfirmationNative(address, confirm, data.intValue());
1271     }
1272 
cancelPairingUserInput(String address)1273     public synchronized boolean cancelPairingUserInput(String address) {
1274         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1275                                                 "Need BLUETOOTH_ADMIN permission");
1276         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1277             return false;
1278         }
1279         mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
1280                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
1281         address = address.toUpperCase();
1282         Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1283         if (data == null) {
1284             Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1285                 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1286                 "by the remote or by bluez.\n");
1287             return false;
1288         }
1289         return cancelPairingUserInputNative(address, data.intValue());
1290     }
1291 
updateDeviceServiceChannelCache(String address)1292     public void updateDeviceServiceChannelCache(String address) {
1293         ParcelUuid[] deviceUuids = getRemoteUuids(address);
1294         // We are storing the rfcomm channel numbers only for the uuids
1295         // we are interested in.
1296         int channel;
1297         if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
1298 
1299         ArrayList<ParcelUuid> applicationUuids = new ArrayList();
1300 
1301         synchronized (this) {
1302             for (RemoteService service : mUuidCallbackTracker.keySet()) {
1303                 if (service.address.equals(address)) {
1304                     applicationUuids.add(service.uuid);
1305                 }
1306             }
1307         }
1308 
1309         Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
1310 
1311         // Retrieve RFCOMM channel for default uuids
1312         for (ParcelUuid uuid : RFCOMM_UUIDS) {
1313             if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1314                 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1315                         uuid.toString(), 0x0004);
1316                 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
1317                 value.put(uuid, channel);
1318             }
1319         }
1320         // Retrieve RFCOMM channel for application requested uuids
1321         for (ParcelUuid uuid : applicationUuids) {
1322             if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1323                 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1324                         uuid.toString(), 0x0004);
1325                 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
1326                 value.put(uuid, channel);
1327             }
1328         }
1329 
1330         synchronized (this) {
1331             // Make application callbacks
1332             for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1333                     iter.hasNext();) {
1334                 RemoteService service = iter.next();
1335                 if (service.address.equals(address)) {
1336                     channel = -1;
1337                     if (value.get(service.uuid) != null) {
1338                         channel = value.get(service.uuid);
1339                     }
1340                     if (channel != -1) {
1341                         if (DBG) log("Making callback for " + service.uuid + " with result " +
1342                                 channel);
1343                         IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1344                         if (callback != null) {
1345                             try {
1346                                 callback.onRfcommChannelFound(channel);
1347                             } catch (RemoteException e) {Log.e(TAG, "", e);}
1348                         }
1349 
1350                         iter.remove();
1351                     }
1352                 }
1353             }
1354 
1355             // Update cache
1356             mDeviceServiceChannelCache.put(address, value);
1357         }
1358     }
1359 
1360     /**
1361      * b is a handle to a Binder instance, so that this service can be notified
1362      * for Applications that terminate unexpectedly, to clean there service
1363      * records
1364      */
addRfcommServiceRecord(String serviceName, ParcelUuid uuid, int channel, IBinder b)1365     public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1366             int channel, IBinder b) {
1367         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1368                                                 "Need BLUETOOTH permission");
1369         if (serviceName == null || uuid == null || channel < 1 ||
1370                 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1371             return -1;
1372         }
1373         if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1374             Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1375             return -1;
1376         }
1377         int handle = addRfcommServiceRecordNative(serviceName,
1378                 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1379                 (short)channel);
1380         if (DBG) log("new handle " + Integer.toHexString(handle));
1381         if (handle == -1) {
1382             return -1;
1383         }
1384 
1385         int pid = Binder.getCallingPid();
1386         mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1387         try {
1388             b.linkToDeath(new Reaper(handle, pid), 0);
1389         } catch (RemoteException e) {}
1390         return handle;
1391     }
1392 
removeServiceRecord(int handle)1393     public void removeServiceRecord(int handle) {
1394         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1395                                                 "Need BLUETOOTH permission");
1396         checkAndRemoveRecord(handle, Binder.getCallingPid());
1397     }
1398 
checkAndRemoveRecord(int handle, int pid)1399     private synchronized void checkAndRemoveRecord(int handle, int pid) {
1400         Integer handleInt = new Integer(handle);
1401         Integer owner = mServiceRecordToPid.get(handleInt);
1402         if (owner != null && pid == owner.intValue()) {
1403             if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
1404                     pid);
1405             mServiceRecordToPid.remove(handleInt);
1406             removeServiceRecordNative(handle);
1407         }
1408     }
1409 
1410     private class Reaper implements IBinder.DeathRecipient {
1411         int pid;
1412         int handle;
Reaper(int handle, int pid)1413         Reaper(int handle, int pid) {
1414             this.pid = pid;
1415             this.handle = handle;
1416         }
binderDied()1417         public void binderDied() {
1418             synchronized (BluetoothService.this) {
1419                 if (DBG) log("Tracked app " + pid + " died");
1420                 checkAndRemoveRecord(handle, pid);
1421             }
1422         }
1423     }
1424 
1425     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1426         @Override
1427         public void onReceive(Context context, Intent intent) {
1428             String action = intent.getAction();
1429             if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1430                 ContentResolver resolver = context.getContentResolver();
1431                 // Query the airplane mode from Settings.System just to make sure that
1432                 // some random app is not sending this intent and disabling bluetooth
1433                 boolean enabled = !isAirplaneModeOn();
1434                 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1435                 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1436                     if (enabled) {
1437                         enable(false);
1438                     } else {
1439                         disable(false);
1440                     }
1441                 }
1442             }
1443         }
1444     };
1445 
registerForAirplaneMode()1446     private void registerForAirplaneMode() {
1447         String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1448                 Settings.System.AIRPLANE_MODE_RADIOS);
1449         mIsAirplaneSensitive = airplaneModeRadios == null
1450                 ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1451         if (mIsAirplaneSensitive) {
1452             mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1453             mContext.registerReceiver(mReceiver, mIntentFilter);
1454         }
1455     }
1456 
1457     /* Returns true if airplane mode is currently on */
isAirplaneModeOn()1458     private final boolean isAirplaneModeOn() {
1459         return Settings.System.getInt(mContext.getContentResolver(),
1460                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1461     }
1462 
1463     /* Broadcast the Uuid intent */
sendUuidIntent(String address)1464     /*package*/ synchronized void sendUuidIntent(String address) {
1465         ParcelUuid[] uuid = getUuidFromCache(address);
1466         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
1467         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
1468         intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1469         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
1470 
1471         if (mUuidIntentTracker.contains(address))
1472             mUuidIntentTracker.remove(address);
1473 
1474     }
1475 
makeServiceChannelCallbacks(String address)1476     /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1477         for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1478                 iter.hasNext();) {
1479             RemoteService service = iter.next();
1480             if (service.address.equals(address)) {
1481                 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
1482                         " " + service.uuid);
1483                 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1484                 if (callback != null) {
1485                     try {
1486                         callback.onRfcommChannelFound(-1);
1487                     } catch (RemoteException e) {Log.e(TAG, "", e);}
1488                 }
1489 
1490                 iter.remove();
1491             }
1492         }
1493     }
1494 
1495     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1496     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1497         switch(mBluetoothState) {
1498         case BluetoothAdapter.STATE_OFF:
1499             pw.println("Bluetooth OFF\n");
1500             return;
1501         case BluetoothAdapter.STATE_TURNING_ON:
1502             pw.println("Bluetooth TURNING ON\n");
1503             return;
1504         case BluetoothAdapter.STATE_TURNING_OFF:
1505             pw.println("Bluetooth TURNING OFF\n");
1506             return;
1507         case BluetoothAdapter.STATE_ON:
1508             pw.println("Bluetooth ON\n");
1509         }
1510 
1511         pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
1512 
1513         pw.println("Local address = " + getAddress());
1514         pw.println("Local name = " + getName());
1515         pw.println("isDiscovering() = " + isDiscovering());
1516 
1517         BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1518 
1519         pw.println("\n--Known devices--");
1520         for (String address : mDeviceProperties.keySet()) {
1521             int bondState = mBondState.getBondState(address);
1522             pw.printf("%s %10s (%d) %s\n", address,
1523                        toBondStateString(bondState),
1524                        mBondState.getAttempt(address),
1525                        getRemoteName(address));
1526 
1527             Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
1528             if (uuidChannels == null) {
1529                 pw.println("\tuuids = null");
1530             } else {
1531                 for (ParcelUuid uuid : uuidChannels.keySet()) {
1532                     Integer channel = uuidChannels.get(uuid);
1533                     if (channel == null) {
1534                         pw.println("\t" + uuid);
1535                     } else {
1536                         pw.println("\t" + uuid + " RFCOMM channel = " + channel);
1537                     }
1538                 }
1539             }
1540             for (RemoteService service : mUuidCallbackTracker.keySet()) {
1541                 if (service.address.equals(address)) {
1542                     pw.println("\tPENDING CALLBACK: " + service.uuid);
1543                 }
1544             }
1545         }
1546 
1547         String value = getProperty("Devices");
1548         String[] devicesObjectPath = null;
1549         if (value != null) {
1550             devicesObjectPath = value.split(",");
1551         }
1552         pw.println("\n--ACL connected devices--");
1553         if (devicesObjectPath != null) {
1554             for (String device : devicesObjectPath) {
1555                 pw.println(getAddressFromObjectPath(device));
1556             }
1557         }
1558 
1559         // Rather not do this from here, but no-where else and I need this
1560         // dump
1561         pw.println("\n--Headset Service--");
1562         switch (headset.getState()) {
1563         case BluetoothHeadset.STATE_DISCONNECTED:
1564             pw.println("getState() = STATE_DISCONNECTED");
1565             break;
1566         case BluetoothHeadset.STATE_CONNECTING:
1567             pw.println("getState() = STATE_CONNECTING");
1568             break;
1569         case BluetoothHeadset.STATE_CONNECTED:
1570             pw.println("getState() = STATE_CONNECTED");
1571             break;
1572         case BluetoothHeadset.STATE_ERROR:
1573             pw.println("getState() = STATE_ERROR");
1574             break;
1575         }
1576 
1577         pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
1578         pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
1579         headset.close();
1580         pw.println("\n--Application Service Records--");
1581         for (Integer handle : mServiceRecordToPid.keySet()) {
1582             Integer pid = mServiceRecordToPid.get(handle);
1583             pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
1584         }
1585     }
1586 
bluezStringToScanMode(boolean pairable, boolean discoverable)1587     /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1588         if (pairable && discoverable)
1589             return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
1590         else if (pairable && !discoverable)
1591             return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
1592         else
1593             return BluetoothAdapter.SCAN_MODE_NONE;
1594     }
1595 
scanModeToBluezString(int mode)1596     /* package */ static String scanModeToBluezString(int mode) {
1597         switch (mode) {
1598         case BluetoothAdapter.SCAN_MODE_NONE:
1599             return "off";
1600         case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
1601             return "connectable";
1602         case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
1603             return "discoverable";
1604         }
1605         return null;
1606     }
1607 
getAddressFromObjectPath(String objectPath)1608     /*package*/ String getAddressFromObjectPath(String objectPath) {
1609         String adapterObjectPath = getProperty("ObjectPath");
1610         if (adapterObjectPath == null || objectPath == null) {
1611             Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1612                     "  or deviceObjectPath:" + objectPath + " is null");
1613             return null;
1614         }
1615         if (!objectPath.startsWith(adapterObjectPath)) {
1616             Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1617                     "  is not a prefix of deviceObjectPath:" + objectPath +
1618                     "bluetoothd crashed ?");
1619             return null;
1620         }
1621         String address = objectPath.substring(adapterObjectPath.length());
1622         if (address != null) return address.replace('_', ':');
1623 
1624         Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1625         return null;
1626     }
1627 
getObjectPathFromAddress(String address)1628     /*package*/ String getObjectPathFromAddress(String address) {
1629         String path = getProperty("ObjectPath");
1630         if (path == null) {
1631             Log.e(TAG, "Error: Object Path is null");
1632             return null;
1633         }
1634         path = path + address.replace(":", "_");
1635         return path;
1636     }
1637 
log(String msg)1638     private static void log(String msg) {
1639         Log.d(TAG, msg);
1640     }
1641 
classInitNative()1642     private native static void classInitNative();
initializeNativeDataNative()1643     private native void initializeNativeDataNative();
setupNativeDataNative()1644     private native boolean setupNativeDataNative();
tearDownNativeDataNative()1645     private native boolean tearDownNativeDataNative();
cleanupNativeDataNative()1646     private native void cleanupNativeDataNative();
getAdapterPathNative()1647     private native String getAdapterPathNative();
1648 
isEnabledNative()1649     private native int isEnabledNative();
enableNative()1650     private native int enableNative();
disableNative()1651     private native int disableNative();
1652 
getAdapterPropertiesNative()1653     private native Object[] getAdapterPropertiesNative();
getDevicePropertiesNative(String objectPath)1654     private native Object[] getDevicePropertiesNative(String objectPath);
setAdapterPropertyStringNative(String key, String value)1655     private native boolean setAdapterPropertyStringNative(String key, String value);
setAdapterPropertyIntegerNative(String key, int value)1656     private native boolean setAdapterPropertyIntegerNative(String key, int value);
setAdapterPropertyBooleanNative(String key, int value)1657     private native boolean setAdapterPropertyBooleanNative(String key, int value);
1658 
startDiscoveryNative()1659     private native boolean startDiscoveryNative();
stopDiscoveryNative()1660     private native boolean stopDiscoveryNative();
1661 
createPairedDeviceNative(String address, int timeout_ms)1662     private native boolean createPairedDeviceNative(String address, int timeout_ms);
cancelDeviceCreationNative(String address)1663     private native boolean cancelDeviceCreationNative(String address);
removeDeviceNative(String objectPath)1664     private native boolean removeDeviceNative(String objectPath);
getDeviceServiceChannelNative(String objectPath, String uuid, int attributeId)1665     private native int getDeviceServiceChannelNative(String objectPath, String uuid,
1666             int attributeId);
1667 
cancelPairingUserInputNative(String address, int nativeData)1668     private native boolean cancelPairingUserInputNative(String address, int nativeData);
setPinNative(String address, String pin, int nativeData)1669     private native boolean setPinNative(String address, String pin, int nativeData);
setPasskeyNative(String address, int passkey, int nativeData)1670     private native boolean setPasskeyNative(String address, int passkey, int nativeData);
setPairingConfirmationNative(String address, boolean confirm, int nativeData)1671     private native boolean setPairingConfirmationNative(String address, boolean confirm,
1672             int nativeData);
setDevicePropertyBooleanNative(String objectPath, String key, int value)1673     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
1674             int value);
createDeviceNative(String address)1675     private native boolean createDeviceNative(String address);
discoverServicesNative(String objectPath, String pattern)1676     /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
1677 
addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, short channel)1678     private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
1679             short channel);
removeServiceRecordNative(int handle)1680     private native boolean removeServiceRecordNative(int handle);
1681 }
1682